本文へ移動
Arduinoのコード・プログラムの書き方。
基礎から応用まで全てがわかる、コピペ可20選。
setup()やloop()、変数、if・for・whileといったプログラムの基礎文法から、サーボやモーターで「動かす」、LCDや7セグメントで「表示する」、ブザーやゲームで「遊ぶ」、シリアル通信やEEPROMで「やり取りする・覚えておく」までを1本にまとめた、Arduino初心者のための決定版ガイドです。AIはコードの下書きやエラーの原因整理を助けてくれる補助ツールとして、実例つきで活用法も紹介します。

Arduinoプログラミングの基礎知識
この先のコードを読む・直す・自分で変更するために必要な、最低限の8つの基礎知識をまとめました。すでに知っている項目は読み飛ばしてかまいません。
① setup()とloop()の意味
Arduinoのスケッチには必ずsetup()とloop()という2つの箱があります。setup()は電源を入れた時に1回だけ実行され、ピンの設定や初期化をします。loop()はその後ずっと繰り返し実行され、実際の動作(読み取り・点灯・判定など)を担当します。
void setup() {
// 電源投入時に1回だけ実行される
pinMode(LED_BUILTIN, OUTPUT);
}
void loop() {
// ここから下は永遠にくり返される
digitalWrite(LED_BUILTIN, HIGH);
delay(500);
digitalWrite(LED_BUILTIN, LOW);
delay(500);
}
② 変数とデータ型
変数は「値を入れておく箱」です。箱には名前と、入れられる値の種類(データ型)があります。よく使う型は下の通りです。
| 型 | 入る値 | 例 |
|---|---|---|
int |
整数(約-32,768〜32,767) | int angle = 90; |
long |
大きい整数 | long total = 100000; |
unsigned long |
0以上の大きい整数(時間管理の定番) | unsigned long now = millis(); |
float |
小数 | float voltage = 3.3; |
bool |
true / falseの2択 | bool isPressed = false; |
char |
文字1つ | char grade = 'A'; |
const int SERVO_PIN = 9;のようにconstを付けると「値を変えない箱(定数)」になり、ピン番号など固定値を安全に管理できます。
③ if、for、while
プログラムの流れを変える3つの基本構文です。
| 構文 | 役割 |
|---|---|
if |
条件が成り立つ時だけ処理をする(「もし〜なら」) |
for |
決まった回数だけくり返す |
while |
条件が成り立つ間、くり返し続ける |
// if:条件分岐
if (digitalRead(2) == LOW) {
// ボタンが押されている時だけここが動く
}
// for:決まった回数くり返す
for (int i = 0; i < 6; i++) {
digitalWrite(LED_PINS[i], HIGH);
}
// while:条件が満たされるまで待つ
while (digitalRead(2) == HIGH) {
// ボタンが押されるまでここでずっと待機
}
④ 関数の作り方
関数は「処理のかたまりに名前を付けたもの」です。同じ処理を何度も書く代わりに、関数を1回作って呼び出すだけで済みます。
// 「戻り値の型」「関数名」「( )の中に受け取る値」を書く
void blinkLed(int pin, int times) {
for (int i = 0; i < times; i++) {
digitalWrite(pin, HIGH);
delay(200);
digitalWrite(pin, LOW);
delay(200);
}
}
void loop() {
blinkLed(8, 3); // 8番ピンのLEDを3回点滅させる
}
voidは「値を返さない」という意味です。値を返す関数を作りたい時はintやboolなど型を指定し、関数の中でreturnを使います。
⑤ digitalReadとanalogRead
どちらも「ピンの状態を読み取る」命令ですが、扱える値の細かさが違います。
| 命令 | 読み取れる値 | 使う例 |
|---|---|---|
digitalRead(pin) |
HIGH(5V)かLOW(0V)の2択 | ボタンが押されているか |
analogRead(pin) |
0〜1023の1024段階 | 可変抵抗の回転量、明るさ、電圧の変化 |
bool pressed = digitalRead(2) == LOW; // ボタン(2値)
int lightLevel = analogRead(A0); // センサー(0〜1023)
analogRead()はA0〜A5などの「アナログ入力ピン」でのみ使えます。デジタルピンでは使えません。
⑥ delayとmillis
delay(ms)は指定したミリ秒間、プログラム全体を完全に止めます。手軽ですが、止まっている間はボタンも読み取れません。millis()は電源投入からの経過時間(ミリ秒)を返すだけの命令で、これを使うと止めずに時間を計ることができます。20選のコードの多くがmillis()方式(ノンブロッキング)を採用しているのはこのためです。
// delay方式:シンプルだが、待っている間は何もできない
digitalWrite(8, HIGH);
delay(1000);
digitalWrite(8, LOW);
// millis方式:待っている間も他の処理(ボタン確認など)ができる
unsigned long lastToggle = 0;
void loop() {
if (millis() - lastToggle >= 1000) {
lastToggle = millis();
digitalWrite(8, !digitalRead(8)); // LEDの状態を反転
}
// ここに他の処理(ボタン確認など)を自由に書ける
}
⑦ エラー文の読み方
Arduino IDEでコンパイルエラーが出たら、まず一番上の赤い行とファイル名・行番号を確認します。よくあるエラーの例です。
| エラー文の例 | だいたいの意味 |
|---|---|
'Servo' was not declared in this scope |
ライブラリの#includeを書き忘れている、または未インストール |
expected ';' before ... |
直前の行の末尾にセミコロン;が抜けている |
expected '}' at end of input |
波かっこ{ }の数が合っていない |
'ledPin' was not declared in this scope |
変数名のスペルミス、または宣言する前に使っている |
avrdude: stk500_recv(): programmer is not responding |
ボードやポートの選択ミス、USB接続やケーブルの問題 |
エラーが複数出ても、まず一番上の1つ目だけを直すことに集中してください。最初のエラーが原因で、その下に連鎖的なエラーが表示されていることがよくあります。
⑧ AIにコードを修正させる方法
エラーや不具合が出た時にAIへ伝える情報は多いほど的確な回答が返ってきます。以下の5点をセットで伝えるのがコツです。
- 使っているボード名(例:Arduino Uno)
- 接続している部品と配線(ピン番号まで)
- 今のコード全文(一部だけだと文脈が伝わりません)
- エラーメッセージ全文、または「症状」(動かない/リセットする/表示が化けるなど)
- すでに試したこと(あれば)
[ここにコード全文を貼り付け]
直った後も「なぜそのエラーが起きたのか」を一言で説明してもらうと、次回自分で気づけるようになります。
出力デバイスを制御するコードの考え方
センサーは「値を読むだけ」なので比較的シンプルですが、サーボモーターやDCモーター、LCDや7セグメントのような出力デバイスは、決まった手順(初期化のおまじない、通信の約束ごと)を守らないと正しく動きません。この「お作法」はライブラリのドキュメントを読めば分かりますが、量が多くて最初は大変です。下の章では、この後に登場する20本のコードに共通する考え方(初期化・状態管理・デバッグの視点)を先にまとめておきます。
公式のライブラリ関数や仕様は、最終的にArduino公式言語リファレンス ↗や各ライブラリのドキュメントで確認してください。
- 初期化の定型処理:Servo.attach()、Wire.begin()などのおまじない部分
- 状態遷移の設計:信号機やゲームのような「今どの状態か」を管理する仕組み
- デバッグの視点:モーターが動かない、表示が化けるといった症状からの原因整理
この記事のコードや解説は、Arduinoの基本的な知識とプログラムを網羅的に示すことを目的としています。AIはあくまで補助的なツールで、初期化コードの下書きやエラーの原因整理を手伝わせると便利、という位置づけです。次章では実例として、AIへの指示(プロンプト)の出し方も紹介します。
サーボ制御プロンプトの設計図
モーターや表示器のようなアクチュエータ系では、「ボード」「部品とピン」「動く条件・動きのパターン」「安全上の制約」を必ず伝えます。
| 要素 | 内容 | 例 |
|---|---|---|
| ボードと制約 | 機種、電源、メモリ | Arduino Uno / 5V USB給電 |
| ハードウェア | アクチュエータ・入力・ピン | SG90サーボ(信号線=D9)、押しボタン(D2) |
| 動きの条件 | 角度、速度、きっかけ | ボタンを押すと0°→90°へゆっくり動く |
| 非機能要件 | delay禁止、コメントなど | millis()で管理、日本語コメント付き |
「どんな速さで」「どのタイミングで」「動作中に別の入力が来たらどうするか」まで指定すると、実際に組み込みやすいコードになります。
生成されたスケッチ全文
プロンプトから生成した「ボタンで開閉する自動ドア風サーボ」のコードです。後半の20選とは別に、AI生成コードを読む教材として掲載しています。
// ボタンで開閉する自動ドア風サーボ
#include <Servo.h>
const int SERVO_PIN = 9;
const int BUTTON_PIN = 2;
const int CLOSED_ANGLE = 0;
const int OPEN_ANGLE = 90;
const unsigned long STEP_INTERVAL = 15; // 1度動かす間隔(ms)
const unsigned long HOLD_OPEN_MS = 3000; // 開いたまま待つ時間
Servo doorServo;
enum DoorState { CLOSED, OPENING, HOLDING, CLOSING };
DoorState state = CLOSED;
int currentAngle = CLOSED_ANGLE;
unsigned long lastStepTime = 0;
unsigned long holdStartTime = 0;
void setup() {
pinMode(BUTTON_PIN, INPUT_PULLUP);
doorServo.attach(SERVO_PIN);
doorServo.write(currentAngle);
Serial.begin(9600);
}
void loop() {
unsigned long now = millis();
bool buttonPressed = digitalRead(BUTTON_PIN) == LOW;
// 閉まっている時だけボタンを受け付ける(動作中は割り込ませない)
if (state == CLOSED && buttonPressed) {
state = OPENING;
Serial.println("Door: opening");
}
if (state == OPENING && now - lastStepTime >= STEP_INTERVAL) {
lastStepTime = now;
currentAngle++;
doorServo.write(currentAngle);
if (currentAngle >= OPEN_ANGLE) {
state = HOLDING;
holdStartTime = now;
Serial.println("Door: holding open");
}
}
if (state == HOLDING && now - holdStartTime >= HOLD_OPEN_MS) {
state = CLOSING;
Serial.println("Door: closing");
}
if (state == CLOSING && now - lastStepTime >= STEP_INTERVAL) {
lastStepTime = now;
currentAngle--;
doorServo.write(currentAngle);
if (currentAngle <= CLOSED_ANGLE) {
state = CLOSED;
Serial.println("Door: closed");
}
}
}
コード解説:ブロック単位で読む
delay()で全体を止めず、少しずつ角度を変えることでゆっくりした動きを表現しています。モーター・表示系コードにありがちな落とし穴
サーボやDCモーターを動かした瞬間にArduinoがリセットすることがあります。多くは電流不足が原因で、外部電源やモータードライバの追加で改善します。
LCDやドットマトリクスなどI2C・SPI通信を使う部品は、アドレスや配線(SDA/SCL、CS/DIN/CLK)を間違えると何も表示されません。型番ごとに配線が違うため必ず確認します。
AIが生成したコードのライブラリ名やクラス名が、実際にインストールしたライブラリと微妙に違うことがあります。ライブラリマネージャーでの正式名称と見比べてください。
実機で検証し、対話で直す
書き込んだ後は必ずシリアルモニタや実際の動きで確認します。「動かない」だけでなく、配線、使用ボード、部品の型番、エラーメッセージ全文をAIへ伝えると修正が早くなります。
20本を書き込む前の共通チェック
- 各コードは1本ずつ別のスケッチとしてArduino IDEへ貼り付ける
- 「ツール → ボード」「ツール → ポート」を実機に合わせる
- Arduinoと外部電源・モジュールのGNDを共通化する
- サーボやDCモーターは、USB給電だけで動かず不安定な場合は外部電源を検討する
- I2C部品(LCD、RTCなど)はSDA・SCLの配線とアドレスを確認する
- 最初はシリアルモニタで状態を表示し、動きを確認しながら調整する
サーボは「Servo」(標準搭載)、ステッピングモーターは「Stepper」(標準搭載)、7セグは「TM1637Display」、LCDは「LiquidCrystal I2C」、ドットマトリクスは「MD_MAX72XX」+「MD_Parola」、RTCは「RTClib」をライブラリマネージャーから追加します。それ以外は標準機能のみです。
コピペですぐ試せるArduinoコード集20選
すべてArduino Uno/一般的なNano互換機を想定した独立スケッチです。今回は「動かす」「表示する」「鳴らす」「遊ぶ・伝える」の4つのテーマでまとめました。検索欄に「サーボ」「LCD」「ゲーム」「通信」などと入力すると対象コードだけに絞れます。
| No. | コード | ライブラリ | 難易度 |
|---|---|---|---|
| 01 | サーボモーターを0〜180度でゆっくり往復させる | Servo | 入門 |
| 02 | DCモーターをL298Nドライバで正転・逆転・速度調整 | 不要 | 入門+ |
| 03 | ステッピングモーター28BYJ-48をULN2003で回す | Stepper | 基礎+ |
| 04 | ブザーで簡単なメロディーを演奏する(オリジナル音階) | 不要 | 入門 |
| 05 | 4桁7セグメントLED(TM1637)でカウントアップタイマー | TM1637Display | 入門+ |
| 06 | 16×2 I2C LCDにメッセージを表示する | LiquidCrystal I2C | 入門+ |
| 07 | 8×8ドットマトリクス(MAX7219)でハートマークを表示 | MD_MAX72XX / MD_Parola | 基礎+ |
| 08 | ミニ信号機シミュレーション(赤・黄・青の状態遷移) | 不要 | 入門 |
| 09 | LEDが左右に流れる「ナイトライダー」風点灯 | 不要 | 入門 |
| 10 | ボタン早押しゲーム(反応速度を測る) | 不要 | 入門+ |
| 11 | LEDを使ったサイコロシミュレーター | 不要 | 入門 |
| 12 | じゃんけんマシン(ボタンでランダムに手を出す) | 不要 | 入門+ |
| 13 | カウントダウンタイマー(0になったらブザーで知らせる) | TM1637Display | 入門+ |
| 14 | モールス信号でメッセージをLEDとブザーで送信 | 不要 | 基礎+ |
| 15 | ボタンの「短押し・長押し・ダブルクリック」を判定 | 不要 | 基礎+ |
| 16 | 2台のArduinoをシリアル通信でつなぐ(送信・受信) | 不要 | 基礎+ |
| 17 | EEPROMにボタンを押した回数を保存し電源を切っても記憶 | EEPROM | 基礎+ |
| 18 | RTCモジュール(DS3231)で現在時刻を表示する簡易時計 | RTClib | 基礎+ |
| 19 | リレーモジュールで豆電球や小型モーターをON/OFF制御 | 不要 | 入門+ |
| 20 | サーボとボタンで「自動ドア」のような開閉デモ | Servo | 基礎+ |
サーボモーターを0〜180度でゆっくり往復させる
角度をゆっくり増減させ、首振りのような動きを作ります。すべてのモーター工作の入り口です。
SG90などの小型サーボモーター
信号線(オレンジ/黄)→D9、+(赤)→5V、-(茶/黒)→GND
コードを表示・コピー
// サーボを0〜180度でゆっくり往復させる
#include <Servo.h>
const int SERVO_PIN = 9;
const unsigned long STEP_INTERVAL = 15; // 角度を1度動かす間隔(ms)
Servo myServo;
int angle = 0;
int step = 1;
unsigned long lastStepTime = 0;
void setup() {
myServo.attach(SERVO_PIN);
myServo.write(angle);
}
void loop() {
unsigned long now = millis();
if (now - lastStepTime >= STEP_INTERVAL) {
lastStepTime = now;
angle += step;
if (angle >= 180 || angle <= 0) {
step = -step; // 端まで来たら向きを反転
}
myServo.write(angle);
}
}
DCモーターをL298Nドライバで正転・逆転・速度調整
2つのピンで回転方向を決め、PWMで速さを変えます。車輪を回す工作の基本です。
DCモーター、L298Nモータードライバモジュール、外部電源
IN1→D8、IN2→D7、ENA→D9(PWM)/L298NのGNDとArduinoのGNDを共通化/モーター用電源はL298NのVCC・GNDへ
コードを表示・コピー
// L298NでDCモーターの向きと速さを制御
const int IN1_PIN = 8;
const int IN2_PIN = 7;
const int ENA_PIN = 9; // PWMで速度調整
void setup() {
pinMode(IN1_PIN, OUTPUT);
pinMode(IN2_PIN, OUTPUT);
pinMode(ENA_PIN, OUTPUT);
}
void spinForward(int speed) {
digitalWrite(IN1_PIN, HIGH);
digitalWrite(IN2_PIN, LOW);
analogWrite(ENA_PIN, speed);
}
void spinBackward(int speed) {
digitalWrite(IN1_PIN, LOW);
digitalWrite(IN2_PIN, HIGH);
analogWrite(ENA_PIN, speed);
}
void stopMotor() {
analogWrite(ENA_PIN, 0);
}
void loop() {
spinForward(150); // ゆっくり正転
delay(2000);
stopMotor();
delay(500);
spinBackward(220); // 速めに逆転
delay(2000);
stopMotor();
delay(500);
}
ステッピングモーター28BYJ-48をULN2003で回す
指定した角度分だけ正確に回転する、位置決めが得意なモーターです。
28BYJ-48ステッピングモーター、ULN2003ドライバボード
ULN2003のIN1〜IN4→D8, D9, D10, D11/ULN2003のVCC・GNDへ外部5V電源(モーターへの給電)を推奨
コードを表示・コピー
#include <Stepper.h>
const int STEPS_PER_REV = 2048; // 28BYJ-48の1回転あたりステップ数
Stepper myStepper(STEPS_PER_REV, 8, 10, 9, 11);
void setup() {
myStepper.setSpeed(10); // 回転速度(rpm)
Serial.begin(9600);
}
void loop() {
Serial.println("Forward 1 turn");
myStepper.step(STEPS_PER_REV); // 1回転、正転
delay(1000);
Serial.println("Backward half turn");
myStepper.step(-STEPS_PER_REV / 2); // 半回転、逆転
delay(1000);
}
ブザーで簡単なメロディーを演奏する(オリジナル音階)
tone()関数で音の高さを変え、オリジナルの短いメロディーを鳴らします。
パッシブブザー
+→D8、-→GND
コードを表示・コピー
// オリジナルの短いメロディーを演奏する
const int BUZZER_PIN = 8;
// ドレミファソラシド(オクターブ4〜5)の周波数(Hz)
int melody[] = {262, 294, 330, 349, 392, 440, 494, 523};
int noteDurations[] = {300, 300, 300, 300, 300, 300, 300, 600};
const int NOTE_COUNT = 8;
void setup() {
pinMode(BUZZER_PIN, OUTPUT);
}
void loop() {
for (int i = 0; i < NOTE_COUNT; i++) {
tone(BUZZER_PIN, melody[i], noteDurations[i]);
delay(noteDurations[i] * 1.2); // 音と音の間に少し隙間を空ける
}
noTone(BUZZER_PIN);
delay(2000);
}
4桁7セグメントLED(TM1637)でカウントアップタイマー
電源を入れてからの経過秒数を、大きな数字表示器にカウントアップ表示します。
TM1637搭載4桁7セグメントLEDモジュール
CLK→D2、DIO→D3、VCC→5V、GND→GND
コードを表示・コピー
#include <TM1637Display.h>
const int CLK_PIN = 2;
const int DIO_PIN = 3;
TM1637Display display(CLK_PIN, DIO_PIN);
unsigned long startTime = 0;
void setup() {
display.setBrightness(5); // 0〜7
startTime = millis();
}
void loop() {
unsigned long elapsedSec = (millis() - startTime) / 1000;
int minutes = (elapsedSec / 60) % 100;
int seconds = elapsedSec % 60;
int displayValue = minutes * 100 + seconds;
display.showNumberDecEx(displayValue, 0b01000000, true); // 中央のコロンを点灯
delay(200);
}
16×2 I2C LCDにメッセージを表示する
2行×16文字の液晶に文字を出す、表示器の定番の一歩目です。
I2Cインターフェース付き16×2キャラクターLCD
SDA→A4、SCL→A5、VCC→5V、GND→GND(Nanoも同じA4/A5)
コードを表示・コピー
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
// アドレスは0x27または0x3Fが多い(不明な場合はI2Cスキャナで確認)
LiquidCrystal_I2C lcd(0x27, 16, 2);
void setup() {
lcd.init();
lcd.backlight();
lcd.setCursor(0, 0);
lcd.print("Hello, Arduino!");
lcd.setCursor(0, 1);
lcd.print("LCD Ready :)");
}
void loop() {
// 表示は固定のまま。動くコードにしたい時はここでlcd.clear()と
// 新しい表示内容を書き直す
}
8×8ドットマトリクス(MAX7219)でハートマークを表示
64個の点を1個ずつON/OFFして、絵文字のような簡単な絵を表示します。
MAX7219搭載8×8ドットマトリクスモジュール
DIN→D11、CS→D10、CLK→D13、VCC→5V、GND→GND
コードを表示・コピー
#include <MD_MAX72xx.h>
#include <SPI.h>
#define HARDWARE_TYPE MD_MAX72XX::FC16_HW
const int DATA_PIN = 11;
const int CLK_PIN = 13;
const int CS_PIN = 10;
const int MAX_DEVICES = 1;
MD_MAX72XX mx = MD_MAX72XX(HARDWARE_TYPE, DATA_PIN, CLK_PIN, CS_PIN, MAX_DEVICES);
// ハート型のパターン(1行8ビット×8行)
byte heart[8] = {
0b01100110,
0b11111111,
0b11111111,
0b11111111,
0b01111110,
0b00111100,
0b00011000,
0b00000000
};
void setup() {
mx.begin();
mx.control(MD_MAX72XX::INTENSITY, 5); // 明るさ 0〜15
for (int row = 0; row < 8; row++) {
mx.setRow(0, row, heart[row]);
}
}
void loop() {
// 表示は固定のまま点灯し続ける
}
ミニ信号機シミュレーション(赤・黄・青の状態遷移)
本物の信号機のように、決まった順番と時間でLEDが切り替わります。
赤LED、黄LED、青LED、220Ω抵抗×3
赤→D5、黄→D6、青→D7(各220Ω経由でGNDへ)
コードを表示・コピー
// 赤→青→黄→赤 の順で切り替わる信号機
const int RED_PIN = 5;
const int YELLOW_PIN = 6;
const int GREEN_PIN = 7;
enum LightState { RED, GREEN, YELLOW };
LightState state = RED;
unsigned long stateStartTime = 0;
void setup() {
pinMode(RED_PIN, OUTPUT);
pinMode(YELLOW_PIN, OUTPUT);
pinMode(GREEN_PIN, OUTPUT);
stateStartTime = millis();
}
void applyState() {
digitalWrite(RED_PIN, state == RED);
digitalWrite(YELLOW_PIN, state == YELLOW);
digitalWrite(GREEN_PIN, state == GREEN);
}
void loop() {
unsigned long elapsed = millis() - stateStartTime;
unsigned long duration =
(state == RED) ? 4000 : (state == GREEN) ? 3000 : 1000;
if (elapsed >= duration) {
if (state == RED) state = GREEN;
else if (state == GREEN) state = YELLOW;
else state = RED;
stateStartTime = millis();
}
applyState();
}
LEDが左右に流れる「ナイトライダー」風点灯
複数のLEDを順番に光らせ、光が左右に走っているように見せます。
LED×6、220Ω抵抗×6
D2〜D7の6ピンに、それぞれLED→220Ω→GNDの順で接続
コードを表示・コピー
// 6個のLEDが左右に往復する光の帯
const int LED_PINS[] = {2, 3, 4, 5, 6, 7};
const int LED_COUNT = 6;
int position = 0;
int direction = 1;
void setup() {
for (int i = 0; i < LED_COUNT; i++) {
pinMode(LED_PINS[i], OUTPUT);
}
}
void loop() {
for (int i = 0; i < LED_COUNT; i++) {
digitalWrite(LED_PINS[i], i == position ? HIGH : LOW);
}
delay(80);
position += direction;
if (position >= LED_COUNT - 1 || position <= 0) {
direction = -direction; // 端で折り返す
}
}
ボタン早押しゲーム(反応速度を測る)
LEDが光ってからボタンを押すまでの時間をミリ秒で測定します。
LEDと押しボタン、220Ω抵抗
LED→D8(220Ω経由)/ボタン→D2(INPUT_PULLUP、反対側はGND)
コードを表示・コピー
// LEDが光ってからボタンを押すまでの時間を測る
const int LED_PIN = 8;
const int BUTTON_PIN = 2;
void setup() {
pinMode(LED_PIN, OUTPUT);
pinMode(BUTTON_PIN, INPUT_PULLUP);
Serial.begin(9600);
randomSeed(analogRead(A0));
}
void loop() {
Serial.println("Get ready...");
digitalWrite(LED_PIN, LOW);
delay(random(2000, 5000)); // ランダムな待ち時間でフライングを防ぐ
digitalWrite(LED_PIN, HIGH);
unsigned long litTime = millis();
Serial.println("GO! Press the button!");
while (digitalRead(BUTTON_PIN) == HIGH) {
// ボタンが押されるまで待つ
}
unsigned long reactionTime = millis() - litTime;
digitalWrite(LED_PIN, LOW);
Serial.print("Reaction time: ");
Serial.print(reactionTime);
Serial.println(" ms");
delay(3000);
}
LEDを使ったサイコロシミュレーター
ボタンを押すと1〜6の数字にあたるLEDパターンが一瞬光ります。
LED×6、220Ω抵抗×6、押しボタン
LED×6→D2〜D7(各220Ω経由)/ボタン→D8(INPUT_PULLUP)
コードを表示・コピー
// ボタンを押すとサイコロの目(LED本数)がランダムに光る
const int LED_PINS[] = {2, 3, 4, 5, 6, 7};
const int BUTTON_PIN = 8;
void setup() {
for (int i = 0; i < 6; i++) pinMode(LED_PINS[i], OUTPUT);
pinMode(BUTTON_PIN, INPUT_PULLUP);
randomSeed(analogRead(A0));
}
void rollDice() {
int result = random(1, 7); // 1〜6
for (int i = 0; i < 6; i++) {
digitalWrite(LED_PINS[i], i < result ? HIGH : LOW);
}
Serial.print("Dice: ");
Serial.println(result);
}
void setupSerial() {
Serial.begin(9600);
}
void loop() {
static bool wasPressed = false;
bool isPressed = digitalRead(BUTTON_PIN) == LOW;
if (isPressed && !wasPressed) {
rollDice();
}
wasPressed = isPressed;
}
じゃんけんマシン(ボタンでランダムに手を出す)
ボタンを押すたびに「グー・チョキ・パー」のどれかをLEDで表示します。
赤LED(グー)、緑LED(チョキ)、青LED(パー)、押しボタン
赤→D5、緑→D6、青→D7(各220Ω経由)/ボタン→D2(INPUT_PULLUP)
コードを表示・コピー
// ボタンを押すとグー・チョキ・パーをランダム表示
const int ROCK_LED = 5;
const int SCISSORS_LED = 6;
const int PAPER_LED = 7;
const int BUTTON_PIN = 2;
const char* HAND_NAMES[] = {"Rock", "Scissors", "Paper"};
bool wasPressed = false;
void setup() {
pinMode(ROCK_LED, OUTPUT);
pinMode(SCISSORS_LED, OUTPUT);
pinMode(PAPER_LED, OUTPUT);
pinMode(BUTTON_PIN, INPUT_PULLUP);
Serial.begin(9600);
randomSeed(analogRead(A0));
}
void showHand(int hand) {
digitalWrite(ROCK_LED, hand == 0);
digitalWrite(SCISSORS_LED, hand == 1);
digitalWrite(PAPER_LED, hand == 2);
Serial.println(HAND_NAMES[hand]);
}
void loop() {
bool isPressed = digitalRead(BUTTON_PIN) == LOW;
if (isPressed && !wasPressed) {
int hand = random(0, 3); // 0:グー 1:チョキ 2:パー
showHand(hand);
}
wasPressed = isPressed;
}
カウントダウンタイマー(0になったらブザーで知らせる)
設定した秒数からカウントダウンし、0になったら音で知らせます。
TM1637搭載4桁7セグメントLEDモジュール、パッシブブザー、押しボタン
CLK→D2、DIO→D3/ブザー→D8/ボタン→D4(INPUT_PULLUP、スタート用)
コードを表示・コピー
#include <TM1637Display.h>
const int CLK_PIN = 2;
const int DIO_PIN = 3;
const int BUZZER_PIN = 8;
const int BUTTON_PIN = 4;
TM1637Display display(CLK_PIN, DIO_PIN);
int secondsLeft = 10; // 10秒からスタート
bool running = false;
unsigned long lastTickTime = 0;
void setup() {
pinMode(BUTTON_PIN, INPUT_PULLUP);
pinMode(BUZZER_PIN, OUTPUT);
display.setBrightness(5);
display.showNumberDec(secondsLeft);
}
void loop() {
if (!running && digitalRead(BUTTON_PIN) == LOW) {
running = true;
secondsLeft = 10;
lastTickTime = millis();
}
if (running && millis() - lastTickTime >= 1000) {
lastTickTime = millis();
secondsLeft--;
display.showNumberDec(secondsLeft);
if (secondsLeft <= 0) {
running = false;
tone(BUZZER_PIN, 1000, 800); // 終了音
}
}
}
モールス信号でメッセージをLEDとブザーで送信
「SOS」を短点・長点のリズムでLEDの点滅とブザー音に変換して送ります。
LED、パッシブブザー、220Ω抵抗
LED→D8(220Ω経由)/ブザー→D9
コードを表示・コピー
// 「SOS」をモールス信号でLEDとブザーで送る
const int LED_PIN = 8;
const int BUZZER_PIN = 9;
const int DOT_MS = 200; // 短点の長さ
const int DASH_MS = 600; // 長点の長さ(短点の3倍)
const int GAP_MS = 200; // 信号同士の間隔
void signalOn(int duration) {
digitalWrite(LED_PIN, HIGH);
tone(BUZZER_PIN, 800);
delay(duration);
digitalWrite(LED_PIN, LOW);
noTone(BUZZER_PIN);
delay(GAP_MS);
}
void sendDot() { signalOn(DOT_MS); }
void sendDash() { signalOn(DASH_MS); }
void setup() {
pinMode(LED_PIN, OUTPUT);
pinMode(BUZZER_PIN, OUTPUT);
}
void loop() {
// S = ・・・
sendDot(); sendDot(); sendDot();
delay(400);
// O = ー ー ー
sendDash(); sendDash(); sendDash();
delay(400);
// S = ・・・
sendDot(); sendDot(); sendDot();
delay(2000); // メッセージ間の長い休み
}
ボタンの「短押し・長押し・ダブルクリック」を判定
1つのボタンで3種類の操作を区別する、実用的な入力判定の仕組みです。
押しボタン、LED
ボタン→D2(INPUT_PULLUP)/LED→D8(動作確認用、任意)
コードを表示・コピー
// 1個のボタンで短押し・長押し・ダブルクリックを判定
const int BUTTON_PIN = 2;
const int LED_PIN = 8;
const unsigned long LONG_PRESS_MS = 600;
const unsigned long DOUBLE_CLICK_GAP_MS = 300;
int lastState = HIGH;
unsigned long pressStartTime = 0;
unsigned long lastReleaseTime = 0;
bool waitingSecondClick = false;
void setup() {
pinMode(BUTTON_PIN, INPUT_PULLUP);
pinMode(LED_PIN, OUTPUT);
Serial.begin(9600);
}
void loop() {
int state = digitalRead(BUTTON_PIN);
unsigned long now = millis();
if (lastState == HIGH && state == LOW) {
pressStartTime = now; // 押された瞬間
}
if (lastState == LOW && state == HIGH) {
unsigned long pressDuration = now - pressStartTime;
if (pressDuration >= LONG_PRESS_MS) {
Serial.println("Long press");
digitalWrite(LED_PIN, HIGH);
delay(200);
digitalWrite(LED_PIN, LOW);
} else if (waitingSecondClick &&
now - lastReleaseTime <= DOUBLE_CLICK_GAP_MS) {
Serial.println("Double click");
waitingSecondClick = false;
} else {
waitingSecondClick = true;
lastReleaseTime = now;
}
}
if (waitingSecondClick && now - lastReleaseTime > DOUBLE_CLICK_GAP_MS) {
Serial.println("Single click");
waitingSecondClick = false;
}
lastState = state;
}
2台のArduinoをシリアル通信でつなぐ(送信・受信)
1台のボタン状態を、もう1台のArduinoへ有線で送ってLEDを光らせます。
Arduinoボード2台、ジャンパー線
送信側D2にボタン/受信側D8にLED/送信側TX(D1)→受信側RX(D0)、GND同士も接続。書き込み時はケーブルを一旦外す
コードを表示・コピー(送信側)
// 送信側:ボタンの状態を1バイトで送る
const int BUTTON_PIN = 2;
void setup() {
pinMode(BUTTON_PIN, INPUT_PULLUP);
Serial.begin(9600);
}
void loop() {
bool pressed = digitalRead(BUTTON_PIN) == LOW;
Serial.write(pressed ? 1 : 0);
delay(100);
}
コードを表示・コピー(受信側)
// 受信側:受け取った値でLEDを点灯
const int LED_PIN = 8;
void setup() {
pinMode(LED_PIN, OUTPUT);
Serial.begin(9600);
}
void loop() {
if (Serial.available() > 0) {
int value = Serial.read();
digitalWrite(LED_PIN, value == 1 ? HIGH : LOW);
}
}
EEPROMにボタンを押した回数を保存し電源を切っても記憶
電源を切っても消えない特別なメモリに、ボタンを押した回数を書き込みます。
押しボタン
ボタン→D2(INPUT_PULLUP、反対側はGND)
コードを表示・コピー
#include <EEPROM.h>
const int BUTTON_PIN = 2;
const int EEPROM_ADDRESS = 0;
int pressCount = 0;
bool wasPressed = false;
void setup() {
pinMode(BUTTON_PIN, INPUT_PULLUP);
Serial.begin(9600);
EEPROM.get(EEPROM_ADDRESS, pressCount); // 前回の続きから読み込む
if (pressCount < 0 || pressCount > 100000) {
pressCount = 0; // 初回など異常値の場合はリセット
}
Serial.print("Loaded count: ");
Serial.println(pressCount);
}
void loop() {
bool isPressed = digitalRead(BUTTON_PIN) == LOW;
if (isPressed && !wasPressed) {
pressCount++;
EEPROM.put(EEPROM_ADDRESS, pressCount); // EEPROMへ書き込み保存
Serial.print("Count: ");
Serial.println(pressCount);
}
wasPressed = isPressed;
}
RTCモジュール(DS3231)で現在時刻を表示する簡易時計
ボタン電池でバックアップされた時計モジュールから時刻を読み取ります。
DS3231 RTCモジュール
SDA→A4、SCL→A5、VCC→5V、GND→GND
コードを表示・コピー
#include <Wire.h>
#include <RTClib.h>
RTC_DS3231 rtc;
void setup() {
Serial.begin(9600);
if (!rtc.begin()) {
Serial.println("RTC not found");
while (true) {}
}
if (rtc.lostPower()) {
// 電池切れ・初回起動時にPCの時刻で初期設定する
rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
}
}
void loop() {
DateTime now = rtc.now();
Serial.print(now.year());
Serial.print("/");
Serial.print(now.month());
Serial.print("/");
Serial.print(now.day());
Serial.print(" ");
Serial.print(now.hour());
Serial.print(":");
Serial.print(now.minute());
Serial.print(":");
Serial.println(now.second());
delay(1000);
}
リレーモジュールで豆電球や小型モーターをON/OFF制御
小さな信号で、大きな電流が必要な機器のスイッチを入り切りします。
リレーモジュール、豆電球や小型モーターなどの負荷、負荷用の専用電源
IN→D8、VCC→5V、GND→GND/負荷側はリレーのCOM・NO端子を経由して専用電源へ接続
コードを表示・コピー
// リレーで5秒ごとにON/OFFを繰り返す
const int RELAY_PIN = 8;
// 多くのモジュールはLOWでON(要確認)
const int RELAY_ON = LOW;
const int RELAY_OFF = HIGH;
void setup() {
pinMode(RELAY_PIN, OUTPUT);
digitalWrite(RELAY_PIN, RELAY_OFF); // 起動直後は必ずOFFにする
}
void loop() {
digitalWrite(RELAY_PIN, RELAY_ON);
delay(5000);
digitalWrite(RELAY_PIN, RELAY_OFF);
delay(5000);
}
このコードは豆電球や小型のおもちゃモーターのような低電圧の負荷を学習目的で試すためのものです。家庭用コンセント(AC100V)や高電圧・大電流の機器を接続する作業は感電・火災の危険があるため、専門知識のある大人が正しい安全対策のもとで行ってください。
サーボとボタンで「自動ドア」のような開閉デモ
「生成されたスケッチ全文」の章で解説した自動ドアのコードを、そのままコピペで使える形にまとめました。
SG90サーボモーター、押しボタン
サーボ信号線→D9、+→5V、-→GND/ボタン→D2(INPUT_PULLUP、反対側はGND)
コードを表示・コピー
// ボタンで開閉する自動ドア風サーボ(コピペ用まとめ版)
#include <Servo.h>
const int SERVO_PIN = 9;
const int BUTTON_PIN = 2;
const int CLOSED_ANGLE = 0;
const int OPEN_ANGLE = 90;
const unsigned long STEP_INTERVAL = 15;
const unsigned long HOLD_OPEN_MS = 3000;
Servo doorServo;
enum DoorState { CLOSED, OPENING, HOLDING, CLOSING };
DoorState state = CLOSED;
int currentAngle = CLOSED_ANGLE;
unsigned long lastStepTime = 0;
unsigned long holdStartTime = 0;
void setup() {
pinMode(BUTTON_PIN, INPUT_PULLUP);
doorServo.attach(SERVO_PIN);
doorServo.write(currentAngle);
}
void loop() {
unsigned long now = millis();
bool buttonPressed = digitalRead(BUTTON_PIN) == LOW;
if (state == CLOSED && buttonPressed) {
state = OPENING;
}
if (state == OPENING && now - lastStepTime >= STEP_INTERVAL) {
lastStepTime = now;
currentAngle++;
doorServo.write(currentAngle);
if (currentAngle >= OPEN_ANGLE) {
state = HOLDING;
holdStartTime = now;
}
}
if (state == HOLDING && now - holdStartTime >= HOLD_OPEN_MS) {
state = CLOSING;
}
if (state == CLOSING && now - lastStepTime >= STEP_INTERVAL) {
lastStepTime = now;
currentAngle--;
doorServo.write(currentAngle);
if (currentAngle <= CLOSED_ANGLE) {
state = CLOSED;
}
}
}
よくある質問とまとめ
プログラミング未経験でもこの記事だけで理解できますか?
はい。前半でsetup()やloop()、変数、if・for・whileといったプログラムの基礎文法を解説しているため、電子工作もプログラミングも初めての方でも順番に読み進めれば理解できる構成にしています。
モーター系のコードで注意することは?
サーボやDCモーター、ステッピングモーターはArduinoのUSB電源だけでは電流が不足することがあります。特にDCモーターやステッピングモーターは、モータードライバと外部電源を使い、GNDをArduinoと共通化してください。
外部ライブラリが必要なコードはありますか?
サーボはServo、ステッピングモーターはStepper、7セグはTM1637Display、LCDはLiquidCrystal I2C、ドットマトリクスはMD_MAX72XXとMD_Parola、RTCはRTClibをArduino IDEのライブラリマネージャーから導入します。それ以外は標準機能だけで動作します。
EEPROMやRTCのコードは電源を切っても大丈夫ですか?
EEPROMは電源を切ってもデータが残る不揮発性メモリです。RTCモジュールはボタン電池でバックアップされており、Arduino本体の電源を切っても時刻を刻み続けます。
まずは部品が少ない「サーボの往復」「信号機」「ナイトライダー」のどれか1本を動かし、動きの仕組みを目で確認するところから始めると理解しやすくなります。動いたコードに対して、AIへ「ボタンで動きを止められるようにして」「LEDをブザーに変えて」「速さを可変抵抗で調整できるようにして」と追加依頼すれば、同じ回路から次の作品へどんどん発展させられます。
コメント