Arduinoの命令一覧:書き方、例、よくある間違い
Arduinoの命令を解説します。pinMode、digitalWrite、analogRead、analogWrite、millis、Serial、map、tone、pulseIn、attachInterruptを、例と落とし穴とあわせて紹介します。
Arduinoの命令は、ボードに何をするかを伝える既製の関数です。pinMode()はピンを出力にし、digitalWrite()はLEDを点け、analogRead()はセンサーを読み、Serial.println()はパソコンにメッセージを送ります。Arduinoのスケッチのほとんどは、実は20個ほどの基本の命令をいろいろに組み合わせたものにすぎません。これらの命令とその意味がわかれば、LEDの点滅からロボットカーまで、目にするどんなコードも読めるようになり、自分のプロジェクトを一から書けるようになります。
このガイドでは、よく使うArduinoの命令を入出力、時間、算術、通信の4つに分類しました。命令ごとに、書き方、パラメーターの意味、Arduino Unoでコンパイルして動く短い例、初心者が最もよくする間違いを紹介します。最後の章では、同じ命令がESP32ではどう違う動きをするか、Raspberry Pi PicoのMicroPythonでは何に相当するかも見ていきます。

Arduinoの命令とは?スケッチはどう動く?
ArduinoのコードはC/C++の上に作られていて、「命令」と呼ぶもののほとんどは、実はArduinoのコアが用意してくれている関数です。関数はかっこの中にパラメーター(たとえばピン番号と値)を受け取り、結果を返すものもあります。digitalRead()はピンの状態を、millis()は経過時間を返します。どのArduinoのスケッチにも、必須の関数が2つあります。setup()はボードの電源が入ったときかリセットされたときに1回だけ動き、そのあとloop()がずっと繰り返されます。ピンの設定やSerial.begin()などの準備はsetup()に、ずっと続けて行う仕事はloop()に書きます。
1// Her Arduino programı bu iki fonksiyondan oluşur / Every Arduino sketch has these two functions
2void setup() {
3 // Açılışta ya da resetten sonra bir kez çalışır / Runs once at power-up or after reset
4 pinMode(13, OUTPUT); // dahili LED pini çıkış / built-in LED pin as output
5}
6
7void loop() {
8 // Sonsuza kadar tekrar eder / Repeats forever
9 digitalWrite(13, HIGH); // LED'i yak / turn the LED on
10 delay(500); // 500 ms bekle / wait 500 ms
11 digitalWrite(13, LOW); // LED'i söndür / turn the LED off
12 delay(500);
13}最初から3つのルールを覚えておくと、多くのコンパイルエラーを防げます。命令は大文字と小文字を区別します(digitalwriteと書くと「'digitalwrite' was not declared in this scope」(宣言されていない)というエラーになります)。どの文もセミコロンで終わります。文字列はダブルクォートで、1文字はシングルクォートで囲みます。変数の型やifやforなどの構造については、Arduinoのプログラミングの基本を見てみましょう。
Arduinoの命令一覧:分類別の表
下の4つの表で、ふだんのプロジェクトで使うほぼすべての命令をまとめています。1つ目のグループは入出力の命令で、ピンの役割を決めたり、電圧をかけたり、値を読んだりします。ボードのピン番号はピン配置ツールで確認できます。
| 命令 | 書き方 | 働き | 例 |
|---|---|---|---|
| pinMode | pinMode(pin, mode) | ピンを入力か出力にする | pinMode(13, OUTPUT); |
| digitalWrite | digitalWrite(pin, value) | ピンをHIGH(5 V)かLOW(0 V)にする | digitalWrite(13, HIGH); |
| digitalRead | digitalRead(pin) | ピンがHIGHかLOWかを読む | int b = digitalRead(2); |
| analogRead | analogRead(pin) | 0〜5 Vの電圧を0〜1023の数値に変える | int v = analogRead(A0); |
| analogWrite | analogWrite(pin, value) | 0〜255のPWM信号を作る | analogWrite(9, 128); |
| analogReference | analogReference(type) | analogReadの基準電圧を選ぶ | analogReference(INTERNAL); |
| tone / noTone | tone(pin, 周波数, 時間) | 方形波で音を出す。noToneで止める | tone(8, 440, 500); |
| pulseIn | pulseIn(pin, 値, タイムアウト) | パルスの長さをµsで測る | pulseIn(10, HIGH, 30000); |
| shiftOut | shiftOut(dataPin, clockPin, 順番, バイト) | 1バイトを1ビットずつ送り出す(例:74HC595へ) | shiftOut(11, 12, MSBFIRST, 255); |
| attachInterrupt | attachInterrupt(割り込み番号, 関数, モード) | ピンが変わった瞬間に関数を呼ぶ | attachInterrupt(digitalPinToInterrupt(2), say, FALLING); |
2つ目のグループは時間の命令です。待つdelay()と、時間を測るmillis()やmicros()の違いを理解することが、なめらかに動くスケッチを書く鍵です。
| 命令 | 書き方 | 働き | 注意点 |
|---|---|---|---|
| delay | delay(ms) | 指定したミリ秒だけ待つ | その間プログラムは止まる |
| delayMicroseconds | delayMicroseconds(us) | マイクロ秒単位で待つ | 3〜16383 µsの間で正確 |
| millis | millis() | 起動からのミリ秒を返す | unsigned long。約49.7日で一周して戻る |
| micros | micros() | 起動からのマイクロ秒を返す | Unoでは4 µs刻み、約70分で一周して戻る |
3つ目のグループは算術の命令で、センサーの値をほかの範囲に変換したり、制限したり、乱数を作ったりするのに使います。4つ目のグループの通信の命令で、ボードはパソコンやほかの機器とデータをやり取りできます。
| 命令 | 書き方 | 働き | 結果の例 |
|---|---|---|---|
| map | map(x, fromLow, fromHigh, toLow, toHigh) | ある範囲を別の範囲に換算する | map(512, 0, 1023, 0, 255) → 127 |
| constrain | constrain(x, 下限, 上限) | 値を2つの限界の間に収める | constrain(300, 0, 255) → 255 |
| min / max | min(a, b) / max(a, b) | 小さいほう、または大きいほうの値を返す | min(7, 3) → 3 |
| abs | abs(x) | 絶対値 | abs(-12) → 12 |
| pow / sqrt / sq | pow(底, 指数)、sqrt(x)、sq(x) | 累乗、平方根、2乗 | pow(2, 10) → 1024.0 |
| random | random(最小, 最大) | ランダムな整数(最大は含まない) | random(1, 7) → 1〜6 |
| randomSeed | randomSeed(seed) | 乱数の並びの出発点を変える | randomSeed(analogRead(A3)); |
| 命令 | 働き | 例 |
|---|---|---|
| Serial.begin(speed) | 指定したボーレートでシリアルポートを開く | Serial.begin(9600); |
| Serial.print(data) | データを表示し、同じ行にとどまる | Serial.print(temperature); |
| Serial.println(data) | データを表示し、改行する | Serial.println("Hello"); |
| Serial.available() | 読み取りを待っているバイト数を返す | if (Serial.available() > 0) |
| Serial.read() | 1バイト読む。データがなければ-1を返す | char c = Serial.read(); |
| Serial.readStringUntil(c) | 指定した文字までの文字列を読む | String s = Serial.readStringUntil('\n'); |
| Serial.parseInt() | 送られてきた文字列から整数を取り出す | int n = Serial.parseInt(); |
| Serial.write(byte) | 生のバイトを送る | Serial.write(65); // 'A' |
| Wire.begin() | I2Cバスを始める(Wire.h) | Wire.begin(); |
| SPI.begin() | SPIバスを始める(SPI.h) | SPI.begin(); |
pinMode():ピンの役割を決める
pinMode(pin, mode)は、デジタルピンが入力か出力かをボードに伝える命令で、ふつうsetup()に1回書きます。モードは3つあります。OUTPUTはピンを出力にし(LED、ブザー、リレーを動かすため)、INPUTはハイインピーダンスの入力にし(外付けのプルアップ抵抗かプルダウン抵抗のある回路用)、INPUT_PULLUPは入力にして、約20〜50 kΩの内部抵抗を5 Vにつなぎます。アナログピン(A0〜A5)もpinModeでデジタルピンとして使えます。たとえばpinMode(A0, OUTPUT)は正しい命令です。
digitalWrite()とdigitalRead():デジタル出力と入力
digitalWrite(pin, HIGH)は、Unoでは出力ピンを5 Vに、digitalWrite(pin, LOW)は0 Vにします。ピンから20 mAまでは安全に取り出せます(絶対最大定格は40 mA)。LEDは必ず抵抗を付けてつなぎましょう。適切な値はLEDの抵抗の計算ツールで求められます。digitalRead(pin)はピンの電圧を見てHIGHかLOWを返します。Unoでは、おおよそ3 Vより上がHIGH、1.5 Vより下がLOWです。下の例では、D2につないだボタンを押すと内蔵LEDが点きます。
1const int BUTON_PIN = 2; // buton: bir ucu D2, diğer ucu GND / button: one leg to D2, the other to GND
2const int LED_PIN = 13; // dahili LED / built-in LED
3
4void setup() {
5 pinMode(BUTON_PIN, INPUT_PULLUP); // dahili pull-up direnci açık / internal pull-up resistor enabled
6 pinMode(LED_PIN, OUTPUT);
7}
8
9void loop() {
10 int durum = digitalRead(BUTON_PIN); // HIGH ya da LOW döner / returns HIGH or LOW
11 if (durum == LOW) { // pull-up ile basılı = LOW / with pull-up, pressed = LOW
12 digitalWrite(LED_PIN, HIGH);
13 } else {
14 digitalWrite(LED_PIN, LOW);
15 }
16}INPUT_PULLUPではロジックが逆になります。ボタンを離しているときは内部抵抗のおかげでピンはHIGHになり、押すとGNDにつながるのでLOWになります。INPUTモードで抵抗なしにボタンをつなぐと、ピンが浮いてdigitalRead()はでたらめな値を返し、手を近づけるだけでも結果が変わることがあります。ボタンのチャタリングと正しいつなぎ方についてはArduinoに押しボタンをつなぐ方法をご覧ください。
analogRead()とanalogWrite():アナログ入力とPWM
analogRead(pin)は10ビットの変換器(ADC)を使い、A0〜A5のピンの0〜5 Vの電圧を0〜1023の数値に変えます。1回の読み取りに約100 µsかかるので、1秒に約10,000回読めます。数値を電圧に直すには値 × 5.0 / 1023を使います。たとえば512なら約2.5 Vです。基準電圧はanalogReference()で変えられます。INTERNALを選ぶとUnoの1.1 Vの内部基準電圧を使い、小さな電圧の分解能が上がります。分解能と基準電圧について詳しくはADCとアナログ・デジタル変換にあります。
名前とは違い、analogWrite(pin, value)は本物のアナログ電圧を作りません。ピンをとても速くオン・オフしてPWM信号を作ります。0はずっとオフ、255はずっとオン、127はおよそ半分オンです。その平均のため、私たちの目にはLEDが半分の明るさに見え、モーターはゆっくり回ります。Unoでは、ボード上で~の付いた3、5、6、9、10、11番のピンだけがPWMを出せます。周波数はほとんどのピンで490 Hz、5番と6番で980 Hzです。analogWrite()はピンを自分で出力に設定するので、pinModeは必須ではありません。下のコードはポテンショメーターでLEDの明るさを決め、読み取った値と電圧をシリアルモニタに表示します。デューティ比と周波数について詳しくはPWMとは?をご覧ください。
1const int POT_PIN = A0; // potansiyometrenin orta bacağı / potentiometer wiper
2const int LED_PIN = 9; // ~9 PWM pini + 220 ohm direnç / ~9 PWM pin + 220 ohm resistor
3
4void setup() {
5 Serial.begin(9600);
6}
7
8void loop() {
9 int ham = analogRead(POT_PIN); // 0-1023 arası / between 0 and 1023
10 float volt = ham * (5.0 / 1023.0); // sayıyı gerilime çevir / convert the reading to volts
11 analogWrite(LED_PIN, ham / 4); // 0-1023 -> 0-255 PWM parlaklığı / 0-1023 -> 0-255 PWM brightness
12
13 Serial.print(F("Ham / raw: "));
14 Serial.print(ham);
15 Serial.print(F(" Gerilim / voltage: "));
16 Serial.print(volt, 2); // 2 ondalık basamak / 2 decimal places
17 Serial.println(F(" V"));
18 delay(200);
19}delay()、millis()、micros():時間の命令
delay(ms)は指定したミリ秒だけ待ち、その間loop()のほかの行は動きません。delay(1000)はまるまる1秒の停止です。マイクロ秒単位の短い待ち時間にはdelayMicroseconds()があり、3〜16383 µsの間で正確です。一方、millis()は待たずに、ボードが起動してからの時間をミリ秒で返します。結果は必ずunsigned longの変数に入れましょう。intを使うと、Unoでは32,767を超えたとたん、約33秒後に値があふれ、タイミングが壊れます。millis()は約49.7日ごとにゼロに戻りますが、下の引き算の方法なら、その一周をまたいでも正しく動き続けます。
1const int LED_PIN = 13;
2const unsigned long ARALIK = 500; // yanıp sönme aralığı (ms) / blink interval (ms)
3unsigned long oncekiZaman = 0; // son değişim anı / time of the last toggle
4
5void setup() {
6 pinMode(LED_PIN, OUTPUT);
7}
8
9void loop() {
10 unsigned long simdi = millis();
11 if (simdi - oncekiZaman >= ARALIK) { // taşmada da doğru çalışır / safe across overflow
12 oncekiZaman = simdi;
13 digitalWrite(LED_PIN, !digitalRead(LED_PIN)); // LED'i tersine çevir / toggle the LED
14 }
15 // Burada buton, sensör ve ekran işleri de yapılabilir / button, sensor and display tasks can run here
16}この形ならloop()は決して止まりません。LEDが点滅し続けている間も、同じループでボタンを読み、センサーを測り、ディスプレイを更新できます。micros()もマイクロ秒単位で同じように動き、Unoでは4 µs刻みで進み、約70分で一周して戻ります。複数の仕事を同時に動かす方法はArduinoのmillis()とdelay()で詳しく解説しています。
Serial.begin()、Serial.print()、Serial.read():シリアル通信
Serialの命令を使うと、ArduinoはUSBケーブルでパソコンと通信でき、これが最も大切なデバッグの道具になります。Serial.begin(9600)は1秒あたり9600ビットでシリアルポートを開きます。シリアルモニタの速度が合っていないと、画面に文字化けした文字が表示されます。Serial.print()はデータを表示して同じ行にとどまり、Serial.println()は表示したあと改行します。2つ目のパラメーターで形式を選べます。Serial.print(78, HEX)は「4E」、Serial.print(78, BIN)は「1001110」を表示し、Serial.print(3.14159, 3)は「3.142」を表示します(小数は初期設定で小数点以下2桁)。固定の文字列をF("...")の中に入れると、Unoの2 KBのRAMを節約できます。
データを読むには、まずSerial.available()でバッファにバイトが待っているかを確認します。Unoの受信バッファは64バイトです。Serial.read()はそのバッファから1バイトを取り出し、読むものがなければ-1を返します。下のコードは、シリアルモニタから送った1と0の文字でLEDをオン・オフします。
1const int LED_PIN = 13;
2
3void setup() {
4 Serial.begin(9600); // Seri Monitör de 9600 olmalı / Serial Monitor must be 9600 too
5 pinMode(LED_PIN, OUTPUT);
6 Serial.println(F("1: LED açık / on, 0: LED kapalı / off"));
7}
8
9void loop() {
10 if (Serial.available() > 0) { // tamponda bayt var mı? / any bytes waiting?
11 char c = Serial.read(); // ilk baytı al / read the first byte
12 if (c == '1') { // '1' karakteri, 1 sayısı değil / the character '1', not the number 1
13 digitalWrite(LED_PIN, HIGH);
14 Serial.println(F("LED açık / on"));
15 } else if (c == '0') {
16 digitalWrite(LED_PIN, LOW);
17 Serial.println(F("LED kapalı / off"));
18 }
19 // '\n' ve '\r' gibi diğer karakterler atlanır / other characters such as '\n' and '\r' are ignored
20 }
21}比較を'1'(シングルクォート、つまり文字)で行っている点に注目してください。キーボードで入力した1はASCIIの表で数値49に相当するので、c == 1という条件は決して成り立ちません。数桁の数値にはSerial.parseInt()が、1行まるごと取り出すにはSerial.readStringUntil('\n')が使えます。シリアルモニタの改行の設定、ボーレート、プロッタについてはArduinoのシリアルモニタの使い方をご覧ください。
map()とconstrain():値の換算と制限
map(value, fromLow, fromHigh, toLow, toHigh)は、数値をある範囲から別の範囲へ比例で移します。裏にある式は(x − fromLow) × (toHigh − toLow) / (fromHigh − fromLow) + toLowです。たとえばmap(512, 0, 1023, 0, 255)は127.6ではなく127を返します。map()は整数だけで計算し、小数を切り捨てるからです。行き先の範囲を入れ替えると、逆の関係を作れます:map(x, 0, 1023, 255, 0)。map()は結果を制限しないので、入力が指定した範囲を外れると、出力も外れます。そのため、ふつうはconstrain(x, low, high)と組み合わせて使います。
1const int LDR_PIN = A0; // LDR + 10k gerilim bölücü / LDR + 10k voltage divider
2const int LED_PIN = 9; // ~9 PWM pini / ~9 PWM pin
3
4void setup() {
5 pinMode(LED_PIN, OUTPUT);
6}
7
8void loop() {
9 int isik = analogRead(LDR_PIN); // odada ölçülen aralık ~200-800 / measured range in the room ~200-800
10 int pwm = map(isik, 200, 800, 255, 0); // karanlıkta parlak, aydınlıkta sönük / bright in the dark, dim in the light
11 pwm = constrain(pwm, 0, 255); // aralık dışı değerleri kes / clip out-of-range values
12 analogWrite(LED_PIN, pwm);
13 delay(20);
14}この例では、LDRは部屋の中で200〜800の値を出します。夜に値が100まで下がると、map()は297を計算します。constrain()がないと、analogWrite()は0〜255の範囲外のこの値を受け取ってしまいます。Unoではこのような値はピンによって違う、予想外の結果になります。8ビットのタイマーを使う3、5、6、11番のピンでは297があふれて41のように振る舞うので、真っ暗なのにLEDは明るく光るどころか、ほとんど消えてしまいます。Unoではconstrain()、min()、max()、abs()がマクロとして定義されているので、かっこの中にx++やほかの関数呼び出しを入れないでください。式が2回以上評価され、予想外の結果になることがあります。LDRの配線はLDR光センサーをご覧ください。
tone()とnoTone():ブザーで音を出す
tone(pin, frequency, duration)は、ピンにデューティ比50%の方形波を作り、パッシブブザーや小さなスピーカーをその周波数で鳴らします。時間はミリ秒単位で、省略できます。省略すると、noTone(pin)を呼ぶまで音が続きます。大切な点として、tone()は待ちません。音を鳴らし始めると、すぐ次の行に進みます。そのため、音符を続けて鳴らすときは、各tone()のあとに少なくとも音符と同じ長さのdelay()が必要です。そうしないと最後の音符しか聞こえません。
1const int BUZZER_PIN = 8; // pasif buzzer'ın + ucu / passive buzzer + leg
2
3void setup() {
4 int notalar[] = {262, 294, 330, 349, 392}; // Do Re Mi Fa Sol (Hz) / C D E F G (Hz)
5 for (int i = 0; i < 5; i++) {
6 tone(BUZZER_PIN, notalar[i], 200); // 200 ms çal / play for 200 ms
7 delay(250); // tone() beklemez, sen bekle / tone() does not wait, so you do
8 }
9 noTone(BUZZER_PIN); // sesi kapat / stop the sound
10}
11
12void loop() {
13}- 周波数の限界:Unoでは31 Hz未満の音は作れず、上限は65,535 Hzです。ブザーで実用的な範囲はおよそ100〜5000 Hzです。
- 音は一度に1つ:一度に音を出せるのは1本のピンだけです。別のピンで音を出すには、まずnoTone()で前の音を止める必要があります。
- PWMとの競合:Unoではtone()がTimer2を使うので、音が鳴っている間は3番と11番のピンのanalogWrite()が乱れます。
- アクティブブザー:自分で発振器を持っているので、tone()で周波数を変えられません。アクティブブザーはdigitalWrite()でオン・オフするだけにしましょう。違いについてはブザーとは?をご覧ください。
pulseIn():パルスの長さを測る
pulseIn(pin, HIGH)は、ピンがHIGHになるのを待ち、HIGHのままでいる時間を測り、結果をマイクロ秒で返します。10 µsから3分までの長さのパルスを測れます。最もよく知られた使い方はHC-SR04超音波距離センサーです。センサーは、音が行って戻ってくるのにかかる時間だけの長さのパルスを出します。音は空気中を約343 m/s、つまり0.0343 cm/µsで進み、往復なので、距離 = 時間 × 0.0343 / 2です。
1const int TRIG_PIN = 9;
2const int ECHO_PIN = 10;
3
4void setup() {
5 Serial.begin(9600);
6 pinMode(TRIG_PIN, OUTPUT);
7 pinMode(ECHO_PIN, INPUT);
8}
9
10void loop() {
11 digitalWrite(TRIG_PIN, LOW);
12 delayMicroseconds(2);
13 digitalWrite(TRIG_PIN, HIGH); // 10 µs tetik darbesi / 10 µs trigger pulse
14 delayMicroseconds(10);
15 digitalWrite(TRIG_PIN, LOW);
16
17 unsigned long sure = pulseIn(ECHO_PIN, HIGH, 30000UL); // en fazla 30 ms bekle / wait at most 30 ms
18 if (sure == 0) {
19 Serial.println(F("Menzil dışı / out of range"));
20 } else {
21 float mesafe = sure * 0.0343 / 2.0; // ses hızı 343 m/s, gidiş-dönüş / speed of sound 343 m/s, round trip
22 Serial.print(mesafe, 1);
23 Serial.println(F(" cm"));
24 }
25 delay(100);
26}3つ目のパラメーターのタイムアウトはとても大切です。これがないと、pulseIn()はパルスが来ないときに1秒待って0を返します。センサーの線が外れると、ループのたびにプログラム全体が1秒止まってしまいます。30000 µsのタイムアウトは約5メートルに相当し、HC-SR04の4メートルの範囲には十分です。センサーの配線と精度についてはHC-SR04距離センサーをご覧ください。
attachInterrupt():割り込みですぐに反応する
attachInterrupt(digitalPinToInterrupt(pin), function, mode)は、loop()がそのとき何をしていても、ピンが変わった瞬間に指定した関数を実行します。この関数を割り込みサービスルーチン(ISR)と呼びます。モードには4つの値があります。RISINGはLOWからHIGHへの変化で、FALLINGはHIGHからLOWへの変化で、CHANGEはすべての変化で、LOWはピンがLOWの間ずっとトリガーします。UnoとNanoでこの方法で割り込みを起こせるのはD2とD3のピンだけです。Megaでは2、3、18、19、20、21番のピンが使えます。速く回るエンコーダー、風速計のパルス、ごく短いボタンの押下を見逃さないのに最適です。
1const int BUTON_PIN = 2; // Uno'da kesme pinleri: 2 ve 3 / interrupt pins on the Uno: 2 and 3
2volatile unsigned long sayac = 0; // ISR'de değişen değişken volatile olmalı / variables changed in an ISR must be volatile
3
4void say() { // kesme fonksiyonu (ISR): kısa tut / interrupt service routine: keep it short
5 sayac++;
6}
7
8void setup() {
9 Serial.begin(9600);
10 pinMode(BUTON_PIN, INPUT_PULLUP);
11 attachInterrupt(digitalPinToInterrupt(BUTON_PIN), say, FALLING); // HIGH'dan LOW'a geçişte / on a HIGH-to-LOW edge
12}
13
14void loop() {
15 noInterrupts(); // 4 baytlık değeri güvenle kopyala / copy the 4-byte value safely
16 unsigned long kopya = sayac;
17 interrupts();
18
19 Serial.print(F("Basma sayısı / presses: "));
20 Serial.println(kopya);
21 delay(500);
22}random()とrandomSeed():乱数を作る
random(max)は0からmax−1までの、random(min, max)はminからmax−1までのランダムな整数を返します。上限は結果に含まれません。そのため、1〜6のサイコロにはrandom(1, 7)と書きます。Arduinoは本物のランダムさを作るのではなく、決まった式で数の並びを計算するので、randomSeed()を呼ばないと、ボードが起動するたびに同じ数が同じ順番で出てきます。何もつないでいないアナログピンのノイズを種(シード)に使えば、実際にはこれを解決できます。
1void setup() {
2 Serial.begin(9600);
3 randomSeed(analogRead(A3)); // boştaki A3 pininin gürültüsü / noise from the unconnected A3 pin
4}
5
6void loop() {
7 int zar = random(1, 7); // 1-6 arası; üst sınır dahil değil / 1 to 6; the upper bound is exclusive
8 Serial.print(F("Zar / dice: "));
9 Serial.println(zar);
10 delay(1000);
11}ESP32でのArduinoの命令:注意すべき点
ESP32もArduino IDEで同じ命令を使ってプログラムでき、この記事のコードのほとんどはピン番号を変えればそのまま動きます。ただし、ハードウェアが違うので、一部の命令は違う動きをします。最も大きな違いは電圧です。ESP32のピンは3.3 Vで動き、digitalWrite(pin, HIGH)は3.3 Vを出力し、5 Vの信号をピンに直接つないではいけません。ボードについてもっと知るにはESP32とは?をご覧ください。
| 命令 | Arduino Uno | ESP32 |
|---|---|---|
| digitalWrite(pin, HIGH) | 5 V | 3.3 V |
| analogRead() | 10ビット、0〜1023、A0〜A5 | 12ビット、0〜4095。Wi-Fiがオンの間はADC1のピン(GPIO 32〜39)のみ |
| analogWrite() | ~のピン(3、5、6、9、10、11)のみ、490/980 Hz | 出力できるほぼすべてのGPIO。周波数はledcAttach(pin, freq, resolution)で設定 |
| attachInterrupt() | D2とD3のみ | すべてのGPIO。ISRにIRAM_ATTRを付ける |
| tone() | Timer2を使い、3番と11番のPWMを乱す | LEDCのハードウェアで動く |
| Serial.begin() | ふつうは9600 | ふつうは115200(起動時のメッセージもこの速度) |
| delay() | プロセッサを待つことだけに完全に使う | 時間をFreeRTOSに渡す。Wi-Fiは裏で動き続ける |
1const int POT_PIN = 34; // ADC1 pini, Wi-Fi açıkken de okur / ADC1 pin, works with Wi-Fi on
2const int LED_PIN = 2; // çoğu DevKit'te dahili LED / onboard LED on most DevKits
3const int BUTON_PIN = 4; // butonun diğer ucu GND / other button leg to GND
4
5volatile bool basildi = false;
6
7void IRAM_ATTR butonISR() { // ESP32'de ISR IRAM'e yerleşmeli / on the ESP32 the ISR should live in IRAM
8 basildi = true;
9}
10
11void setup() {
12 Serial.begin(115200);
13 pinMode(BUTON_PIN, INPUT_PULLUP);
14 attachInterrupt(digitalPinToInterrupt(BUTON_PIN), butonISR, FALLING); // her GPIO kesme destekler / every GPIO supports interrupts
15}
16
17void loop() {
18 int ham = analogRead(POT_PIN); // 0-4095 (12 bit)
19 analogWrite(LED_PIN, map(ham, 0, 4095, 0, 255)); // 8 bit PWM / 8-bit PWM
20 if (basildi) {
21 basildi = false;
22 Serial.println("Butona basıldı / button pressed");
23 }
24 Serial.println(ham);
25 delay(200);
26}ESP32ではanalogRead()が0〜4095を返すので、map()の範囲もそれに合わせて変わります。ISRの前のIRAM_ATTRは、コードを高速な内部メモリに置きます。これがないと、ボードが状況によって「Guru Meditation Error」で再起動することがあります。ESP32のアナログの値は、範囲の両端付近(0 Vと3.3 Vのあたり)では直線的ではありません。より正確に測りたいなら、analogReadMilliVolts()関数がキャリブレーション済みの値をミリボルトで返します。
Raspberry Pi Pico:Arduinoの命令のMicroPythonでの対応
Raspberry Pi PicoをArduino IDE(arduino-picoコア)でプログラムすれば、この記事のすべての命令が同じ名前で動きます。analogRead()は初期設定で10ビットを返し、analogWrite()は1 kHzのPWMを作ります。ただし、ほとんどの人はPicoをMicroPythonで使います。そこでは命令をmachineとtimeのモジュールのオブジェクトで書きます。考え方は同じで、書き方が変わるだけです。Picoのピンも3.3 Vで、アナログ入力はGP26、GP27、GP28のピンです。始めるにはRaspberry Pi Picoとは?をご覧ください。
| Arduinoの命令 | MicroPythonでの対応 | メモ |
|---|---|---|
| pinMode(15, OUTPUT) | led = Pin(15, Pin.OUT) | Pinオブジェクトは1回だけ作る |
| pinMode(14, INPUT_PULLUP) | btn = Pin(14, Pin.IN, Pin.PULL_UP) | 内部プルアップ抵抗 |
| digitalWrite(15, HIGH) | led.value(1) | led.on()でもよい |
| digitalRead(14) | btn.value() | 0か1を返す |
| analogRead(A0) | ADC(26).read_u16() | 0〜65535を返す |
| analogWrite(15, 128) | PWM(Pin(15)).duty_u16(32768) | デューティは0〜65535 |
| delay(500) | time.sleep_ms(500) | time.sleep(0.5)でもよい |
| millis() | time.ticks_ms() | 差にはtime.ticks_diff()を使う |
| Serial.println(x) | print(x) | Thonnyのシェルに表示される |
| tone(pin, 440) | pwm.freq(440)とpwm.duty_u16(32768) | duty_u16(0)で音を止める |
| pulseIn(pin, HIGH, 30000) | machine.time_pulse_us(pin, 1, 30000) | タイムアウトのときはマイナスの値を返す |
| attachInterrupt(...) | btn.irq(trigger=Pin.IRQ_FALLING, handler=f)(割り込みの設定) | fはパラメーターとしてピンのオブジェクトを受け取る |
| random(1, 7) | random.randint(1, 6) | 上限を含む |
| map() / constrain() | 組み込みではないので自分で書く | constrainなら:min(max(x, 0), 255) |
1from machine import Pin, ADC, PWM
2import time
3
4led = PWM(Pin(15)) # pinMode + analogWrite karşılığı / pinMode + analogWrite equivalent
5led.freq(1000) # 1 kHz PWM
6buton = Pin(14, Pin.IN, Pin.PULL_UP) # INPUT_PULLUP karşılığı / INPUT_PULLUP equivalent
7pot = ADC(26) # GP26 = ADC0
8
9def map_deger(x, giris_min, giris_max, cikis_min, cikis_max): # Arduino map() karşılığı / Arduino map() equivalent
10 return (x - giris_min) * (cikis_max - cikis_min) // (giris_max - giris_min) + cikis_min
11
12onceki = time.ticks_ms() # millis() karşılığı / millis() equivalent
13while True:
14 ham = pot.read_u16() # 0-65535 (analogRead)
15 led.duty_u16(ham) # parlaklık / brightness (analogWrite)
16 if buton.value() == 0: # digitalRead; basılı = 0 / pressed = 0
17 print("Butona basıldı / button pressed")
18 if time.ticks_diff(time.ticks_ms(), onceki) >= 1000: # saniyede bir / once a second
19 onceki = time.ticks_ms()
20 print("Yüzde / percent:", map_deger(ham, 0, 65535, 0, 100))
21 time.sleep_ms(50) # delay(50)表のとおり、MicroPythonのrandom.randint(1, 6)は上限を含み、Arduinoのrandom(1, 7)と同じ結果になります。time.ticks_diff()は、millis()の引き算と同じく一周への安全性を持っています。この小さな違いが、2つのプラットフォームを行き来するときに最も混乱しやすい点です。MicroPythonのほかの基本の命令については、MicroPythonの基本を見てみましょう。
これらの命令で作れるプロジェクトのアイデア
- 信号機:pinMode()、digitalWrite()、delay()で3つのLEDを順番に点けます。Arduinoで信号機でステップごとに解説しています。
- デジタルサイコロ:random()で1〜6の数を作り、ボタンを押したら結果をLEDか7セグメントディスプレイに表示します。
- 駐車センサー:pulseIn()で距離を測り、map()でビープの間隔に変え、tone()で警告音を鳴らします。
- 自動ナイトライト:analogRead()で明るさを読み、map()とconstrain()でanalogWrite()の明るさを決めます。
- シリアルポートのリモコン:Serial.read()を使い、パソコンからの命令でLED、リレー、サーボモーターを制御します。
- 反応テスト:random()でランダムなタイミングにLEDを点け、attachInterrupt()とmillis()でボタンを押すまでの反応時間をミリ秒単位で測ります。
よくある間違いと対処法
| 間違い | 症状 | 対処法 |
|---|---|---|
| pinMode()を忘れた | LEDがとても暗い、またはまったく点かない | setup()にpinMode(pin, OUTPUT)を書く |
| INPUTモードで抵抗なしのボタン | digitalRead()がでたらめに0と1を返す | INPUT_PULLUPを使う。押すとLOWになる |
| 命令の名前の打ち間違い | 'digitalwrite' was not declared in this scope(宣言されていない) | 大文字と小文字に注意:digitalWrite |
| ボーレートの不一致 | シリアルモニタに文字化けした文字 | シリアルモニタの速度をSerial.begin()に合わせる |
| PWMのないピンでanalogWrite() | LEDが完全に点くか消えるかだけ | ~の付いたピン(3、5、6、9、10、11)を使う |
| millis()をintに入れた | 約33秒後にタイミングが壊れる | unsigned longを使う |
| random(1, 6)でサイコロ | 6が決して出ない | random(1, 7)と書く。上限は含まれない |
| map()に小数を期待した | 結果がいつも整数で、小さな変化が失われる | 必要なら自分で小数の式を書く |
| タイムアウトなしのpulseIn() | センサーがないとループのたびに1秒止まる | 3つ目のパラメーターに30000を渡す |
| ISRの中でdelay()やSerial | プログラムが止まる、またはおかしな動きをする | ISRではvolatileのフラグを変えるだけにする |
この記事の命令は、Arduinoで作るほとんどのプロジェクトの部品です。それ以外は、たいていライブラリが用意する追加の関数です。それぞれの命令を専用の小さな回路で一度試し、結果をシリアルモニタで見ると、暗記するよりずっとよく身につきます。BlueGraysアプリでは、LEDの点灯、ボタンでのLEDの制御、LDRでの明るさの測定、ブザーの周波数の設定、HC-SR04での距離の測定、サイコロのプロジェクトなど、これらの命令を使うプロジェクトを、回路図とコメント付きのコードでステップごとに学べます。
よくある質問
最もよく使うArduinoの命令は?
最もよく使うのは、pinMode()、digitalWrite()、digitalRead()、analogRead()、analogWrite()、delay()、millis()、Serial.begin()、Serial.println()です。値の換算にmap()、音にtone()、距離センサーにpulseIn()、割り込みにattachInterrupt()を加えれば、初心者向けのプロジェクトはほぼすべて書けます。
Arduinoの命令は何の言語で書かれていますか?
ArduinoのコードはCとC++の上に作られていて、Arduinoのコアが言語にpinMode()やdigitalWrite()などの既製の関数を追加しています。命令は英語で、大文字と小文字を区別し、どの文もセミコロンで終わります。ESP32とRaspberry Pi Picoは、好みに応じてMicroPythonでもプログラムできます。
digitalWriteとanalogWriteの違いは何ですか?
digitalWrite()はピンをHIGH(Unoでは5 V)かLOW(0 V)の2つの状態のどちらかにするだけです。analogWrite()はピンをとても速くオン・オフして0〜255のPWM信号を作るので、LEDの明るさやモーターの速さを調整できます。Unoでは、analogWrite()は~の付いたピンでしか動きません。
void setupとvoid loopの意味は?
setup()は、ボードの電源が入ったときかリセットされたときに1回動く関数で、pinMode()やSerial.begin()などの準備をここに書きます。loop()は、setup()が終わったあとずっと繰り返される関数です。前に付いているvoidは、これらの関数が値を返さないという意味です。
Serial.printとSerial.printlnの違いは何ですか?
Serial.print()はデータを表示してカーソルを同じ行に残すので、次のデータはそのすぐ後ろに続きます。Serial.println()は表示したあとに改行の文字(\r\n)を送るので、次のデータは新しい行から始まります。1行に複数の値を並べて表示するには、まずprint()を使い、最後にprintln()を使いましょう。
Arduinoのmap関数は何をしますか?
map()は、数値をある範囲から別の範囲へ比例で変換します。たとえば、analogRead()の0〜1023の値をanalogWrite()用の0〜255の範囲や、サーボ用の0〜180度に変えます。整数だけで計算し、結果を制限しないので、範囲外の入力に備えてconstrain()と組み合わせることがすすめられています。
この記事を読んだ後に作れるプロジェクト
全プロジェクトを見る
LEDの点滅
電子工作の第一歩は、1個のLEDを点けたり消したりすることです。Arduinoで短い間隔で点灯と消灯を繰り返すと、LEDが素早く点滅します。ボードが光を制御する仕組みを最もシンプルな形で確かめ、これから取り組むプロジェクトの基礎を身につけます。

ボタンでLEDを点灯(押している間のみ)
呼び鈴のように、押している間は点灯し、離すと消灯します。Arduinoはボタンの状態を常に読み取り、押されている間はLEDを点灯させ、指を離した瞬間に消灯させます。このシンプルな回路で、ボードが外部からの操作を検知する仕組みを学べます。

LDRで光の強さを測定
部屋の明るさを数値で見てみませんか?光に反応する小さな部品、LDRで周囲の光を測定し、Arduinoが0.5秒ごとにその値をコンピューターの画面に表示します。手で覆うと数値が変わる様子を確認できます。自動で点灯する常夜灯などのプロジェクトへの第一歩です。

ブザーの周波数を調整
4つのボタンでブザーの音を操作します。1つは音を鳴らし、1つは音を止め、残りの2つは音を高くしたり低くしたりします。音を高くするボタンや低くするボタンを押し続けると、音程が段階的に変化し、サイレンのように聞こえます。音の高低が生まれる理由を、自分の耳で確かめられます。

HC-SR04で距離を測定(シリアルモニタ表示)
HC-SR04センサーは、コウモリが暗闇で道を見つけるのと同じ仕組みで動作します。人には聞こえない音を出し、その反響を捉えて前方の物体までの距離を測ります。Arduinoは0.5秒ごとに距離をセンチメートル単位でコンピューターの画面に表示し、3〜100センチメートルの測定値を示します。駐車センサーや障害物を避けるロボットなどのプロジェクトの基礎になります。

速度調整できるKnight Rider風LED
6個のLEDの上を光が端から端へ走り、折り返して戻ってきます。ポテンショメーターのつまみを回すと、その速さをすぐに調整できます。つまみを一方の端にすると光が飛ぶように進み、反対の端にするとゆっくり進みます。1つのつまみで動作中のアニメーションを操る楽しさを体験できます。

ポテンショメーターでサーボモーターを操作
ポテンショメーターのつまみを回すと、サーボモーターのアームもそれに合わせて回ります。つまみの一方の端が0度、もう一方の端が半回転に対応します。Arduinoがつまみの位置を常に読み取り、サーボモーターが動きにすぐ反応します。ロボットアームや回転式カメラ台などのプロジェクトへの第一歩です。

サイコロのプロジェクト
ボードゲームでサイコロをなくす心配がなくなります。ボタンを押すたびに、7セグメントディスプレイに1から6までのランダムな数字が表示されます。1回押すと結果が1つだけ出て、コンピューターの画面にも表示されます。友達とのゲームで使える、手作りのデジタルサイコロです。
ほかの記事
BlueGraysアプリ
この回路をスマホでステップごとに作る
回路図、コード、部品リストを1つの画面で確認できます。Arduino、ESP32、Picoのプロジェクトを200件以上用意しています。

