Arduinoで複数センサーを同時に使う方法|値の取得・配列・タイミング制御

プログラミング・電子工作






Arduinoで複数センサーを同時に使う方法|値の取得・配列・タイミング制御







実践ガイド / 保存版

Arduinoで複数センサーを同時に使う方法|値の取得・配列・タイミング制御

センサー1つなら簡単に動かせても、2つ、3つと増えていくと、とたんにコードがぐちゃぐちゃになったり、動きがおかしくなったりしませんか?本記事では、複数のセンサーを無理なく同時に扱うための「値の取得方法」「配列での管理」「delay()を使わないタイミング制御」のコツを、子供でもわかるように、ひとつずつ丁寧に解説していきます。

YOFUKASI LAB編集部
公開日:2026年10月4日
読了目安:16分

執筆・実機検証:YOFUKASI LAB運営者

理学療法士/Arduino・Pythonを使った医療機器制作・学会発表経験あり

🔧 この記事でわかること

気になる項目から読んでも大丈夫です。

  • Arduinoの「同時」の本当の仕組み → 1章へ
  • 配列を使ったセンサー値の一元管理 → 2章へ
  • 複数のアナログセンサーを読む実践コード → 3章へ
  • 複数のデジタルセンサーを読む実践コード → 4章へ
  • delay()を使わないタイミング制御 → 5章へ
  • 配線・電源の注意点 → 6章へ
  • 同じI2Cセンサーを複数つなぐ方法 → 7章へ
Chapter 01 / How “Simultaneous” Really Works

01大前提:Arduinoの「同時」は超高速の「順番」

複数センサーを扱う前に、まず知っておきたい、Arduinoの内部の仕組みです。

Arduinoのアナログ入力をA0・A1・A2の順番で高速に読み取る仕組み

かんたん説明
Arduino UNOは、アナログの値を読み取る「物差し」を、実は1本しか持っていません(これをADC=アナログ・デジタル変換器と呼びます)。A0、A1、A2…という複数のアナログピンがあっても、裏側ではこの1本の物差しを、ものすごい速さで順番に使い回しているのです。1回測るのにかかる時間は、わずか約100マイクロ秒(1万分の1秒)。人間の目には同時に見えますが、実際には「超高速の順番待ち」が起きています。
読み取り方法 1回あたりの時間 特徴
analogRead() 約100マイクロ秒 内部のADC1つを複数ピンで共有しているため、この時間が必ずかかる
digitalRead() 1マイクロ秒未満 ON/OFFの判定だけなので、ADCを使わず非常に高速

センサーが3つ、4つと増えても、1つあたり約100マイクロ秒程度なので、合計しても1ミリ秒に満たないことがほとんどです。つまり、「複数のアナログセンサーを順番に読む」こと自体は、速度の面ではほとんど問題になりません。本当に気をつけるべきなのは、この後に説明する「delay()を使った待ち時間」や「配線・電源」の方です。

ただし例外もあります。超音波距離センサー(HC-SR04など)で使うpulseIn()という関数は、電気信号が戻ってくるまで数十〜数万マイクロ秒、プログラムの実行を止めて待ち続けます。こうした「待たされる」命令が混ざっている場合は、後述する5章のタイミング制御がとても重要になります。



Chapter 02 / Managing Values with Arrays

02配列(array)でセンサー値を一元管理する

センサーが増えるほど、変数をバラバラに管理すると大変になります。そこで役立つのが「配列」です。

Arduinoで配列arrayを使って複数センサーの値を一元管理する方法

かんたん説明
配列(array)は、同じ種類の箱を、番号付きでまとめて並べた「ロッカー」のようなものです。int sensorValues[3];と書くと、「0番・1番・2番」という3つの箱(ロッカー)がまとめて用意されます。センサーが1個増えるたびに新しい変数名(sensor1, sensor2, sensor3…)を考える必要がなくなり、「for文」を使って全部の箱に一気にアクセスできるようになります。

変数を個別に書く場合(センサーが増えると大変)

// センサーが3つあると、変数も3つ必要
int sensorValueA0 = analogRead(A0);
int sensorValueA1 = analogRead(A1);
int sensorValueA2 = analogRead(A2);

Serial.print(sensorValueA0);
Serial.print(",");
Serial.print(sensorValueA1);
Serial.print(",");
Serial.println(sensorValueA2);

配列を使う場合(センサーが増えても書き方は変わらない)

// 使うピンの一覧を配列にまとめる
const int sensorPins[] = {A0, A1, A2};
const int NUM_SENSORS = 3;   // センサーの数
int sensorValues[NUM_SENSORS]; // 読み取った値を入れる箱(ロッカー)

void setup() {
  Serial.begin(9600);
}

void loop() {
  // for文で全センサーを順番に読み取る
  for (int i = 0; i < NUM_SENSORS; i++) {
    sensorValues[i] = analogRead(sensorPins[i]);
  }

  // まとめて表示する
  for (int i = 0; i < NUM_SENSORS; i++) {
    Serial.print(sensorValues[i]);
    if (i < NUM_SENSORS - 1) Serial.print(",");
  }
  Serial.println();

  delay(200);
}

このコードの良いところは、センサーを4つ、5つに増やしたいときも、sensorPins[]の中身とNUM_SENSORSの数字を変えるだけで済む点です。for文の中身を書き直す必要はありません。センサーの種類が増えても、管理すべき場所が1箇所に集約されるため、バグも減らせます。

Chapter 03 / Reading Multiple Analog Sensors

03複数のアナログセンサーを同時に読む

実際によく使われる、複数のアナログセンサーの組み合わせで試してみましょう。

ArduinoでCdS光センサー・可変抵抗・土壌湿度センサーを同時に読み取る方法

1センサーを選ぶ・配線する

例として、CdS光センサー(明るさ)、可変抵抗(ボリューム)、土壌湿度センサーの3つを、それぞれA0・A1・A2につなぐ構成を考えます。アナログセンサーの多くは「信号線をアナログピンへ、+を5Vへ、-をGNDへ」という共通の配線パターンです。

2配列で一括読み取り

2章で紹介した配列の書き方をそのまま使い、3つのセンサーをまとめて読み取ります。

const int sensorPins[] = {A0, A1, A2};
const char* sensorNames[] = {"明るさ", "ボリューム", "土壌湿度"};
const int NUM_SENSORS = 3;
int sensorValues[NUM_SENSORS];

void setup() {
  Serial.begin(9600);
}

void loop() {
  for (int i = 0; i < NUM_SENSORS; i++) {
    sensorValues[i] = analogRead(sensorPins[i]);
  }

  for (int i = 0; i < NUM_SENSORS; i++) {
    Serial.print(sensorNames[i]);
    Serial.print(": ");
    Serial.print(sensorValues[i]);
    Serial.print("  ");
  }
  Serial.println();

  delay(300);
}
かんたん説明
名前の配列(sensorNames[])も一緒に用意しておくと、シリアルモニタに「明るさ: 512」のように、何の値かひと目でわかる形で表示できます。センサーが増えても、どの数字がどのセンサーのものか迷わなくなる、小さな工夫です。


Chapter 04 / Reading Multiple Digital Sensors

04複数のデジタルセンサーを同時に読む

タクトスイッチや人感センサーなど、ON/OFFのデジタルセンサーを複数扱う場合も、考え方は同じです。

Arduinoで複数のデジタルセンサーをdigitalReadで読み取る方法

const int switchPins[] = {2, 3, 4};   // タクトスイッチ3個
const int NUM_SWITCHES = 3;
int switchStates[NUM_SWITCHES];

void setup() {
  Serial.begin(9600);
  for (int i = 0; i < NUM_SWITCHES; i++) {
    pinMode(switchPins[i], INPUT_PULLUP); // 内部プルアップ抵抗を使う
  }
}

void loop() {
  for (int i = 0; i < NUM_SWITCHES; i++) {
    switchStates[i] = digitalRead(switchPins[i]);
  }

  for (int i = 0; i < NUM_SWITCHES; i++) {
    // INPUT_PULLUPは「押すとLOW」になる点に注意
    Serial.print(switchStates[i] == LOW ? "押されている" : "離されている");
    Serial.print("  ");
  }
  Serial.println();

  delay(50);
}
かんたん説明
INPUT_PULLUPを使うと、スイッチにつなぐ配線を1本減らせる(抵抗を別途用意しなくて済む)便利な設定です。ただし、ボタンを「押したとき」に電気の値がLOW(0)になるという、直感とは逆の動きをする点に注意してください。慣れないうちは混乱しやすいポイントです。

digitalRead()はADCを使わないため、1章で説明した「順番待ち」のロスがほとんどありません。タクトスイッチやPIR人感センサー(HC-SR501)のようなON/OFFセンサーなら、何個増やしても速度面の心配はほぼ不要です。

Chapter 05 / Timing Control Without delay()

05delay()を使わないタイミング制御

複数センサーを扱うときに、もっともつまずきやすいのが「タイミング制御」です。ここが本記事の核心です。

Arduinoのdelayとmillisの違いと複数センサーのタイミング制御

かんたん説明
delay(1000)と書くと、Arduinoはその1000ミリ秒(1秒)の間、他のことを一切できなくなります。たとえ話で言うと、「1人の店員さんが、1人のお客さんの対応に1分間、完全に付きっきりになる」ようなイメージです。その間、他のお客さん(他のセンサー)は、ずっと無視されたままになってしまいます。

温度センサーは2秒に1回、光センサーは0.5秒に1回、距離センサーは0.1秒に1回……のように、センサーごとに「ちょうどいい確認頻度」が違うことはよくあります。それぞれにdelay()を使うと、合計の待ち時間が足し算されてしまい、全体の反応がどんどん遅くなります。この問題を解決するのが、millis()という「経過時間を測る関数」を使った書き方です。

millis()を使った非ブロッキング(止まらない)タイミング制御

unsigned long lastTempRead = 0;
unsigned long lastLightRead = 0;
unsigned long lastDistanceRead = 0;

const unsigned long TEMP_INTERVAL = 2000;      // 2秒ごと
const unsigned long LIGHT_INTERVAL = 500;      // 0.5秒ごと
const unsigned long DISTANCE_INTERVAL = 100;   // 0.1秒ごと

void loop() {
  unsigned long now = millis(); // 現在の経過時間を取得

  // 温度センサー(2秒ごと)
  if (now - lastTempRead >= TEMP_INTERVAL) {
    lastTempRead = now;
    int tempValue = analogRead(A0);
    Serial.print("温度: ");
    Serial.println(tempValue);
  }

  // 光センサー(0.5秒ごと)
  if (now - lastLightRead >= LIGHT_INTERVAL) {
    lastLightRead = now;
    int lightValue = analogRead(A1);
    Serial.print("明るさ: ");
    Serial.println(lightValue);
  }

  // 距離センサー(0.1秒ごと)
  if (now - lastDistanceRead >= DISTANCE_INTERVAL) {
    lastDistanceRead = now;
    int distanceValue = analogRead(A2);
    Serial.print("距離: ");
    Serial.println(distanceValue);
  }

  // delay()を使わないので、loop()は常にぐるぐる回り続ける
}

1仕組みのポイント

それぞれのセンサーに「最後に読み取った時刻(lastXxxRead)」を覚えさせておき、「現在時刻 − 最後に読み取った時刻 ≧ 決めた間隔」になったときだけ処理を実行します。これにより、loop()自体は止まらずに回り続けながら、センサーごとに異なるペースで処理を行えます。

!

pulseIn()を使う超音波センサーは要注意

HC-SR04などの超音波距離センサーで使うpulseIn()は、1章で触れた通り、戻ってくるまでプログラムを止めて待つ命令です。他の処理をmillis()で非ブロッキング化していても、pulseIn()自体は短時間ブロックすることを理解しておく必要があります。タイムアウト値(例:pulseIn(echoPin, HIGH, 30000)のように第3引数で上限を指定)を設定し、待ち時間に上限をつけておくと安心です。



Chapter 06 / Wiring & Power Considerations

06配線・電源面の注意点

センサーが増えると、ソフトだけでなく「配線」と「電源」にも気を配る必要が出てきます。

Arduinoで複数センサーを接続するときの配線と外部電源・共通GNDの注意点

1供給できる電流には上限がある

Arduino UNOをパソコンのUSBポートから電源供給している場合、全体で使える電流には上限があります。センサー1つなら気にならなくても、サーボモーターやモーターなど電気を多く使う部品を複数つなぐと、供給が追いつかず、動作が不安定になったり、Arduino自体が再起動してしまうことがあります。

2電源を分ける・外部電源を使う

モーターやサーボなど電流を多く使う部品は、Arduinoの5V端子から直接取るのではなく、別の電源(外部電源)を用意し、GND(マイナス)だけは必ずArduinoと共通にする、という配線が基本です。

3配線とブレッドボードの整理

センサーが3つ、4つになると、配線もそれだけ増えます。ブレッドボードの電源レール(赤・青のライン)を使って、5VとGNDをまとめてから、各センサーに枝分かれさせるように配線すると、見通しがよくなり、ミスも減らせます。

かんたん説明
電源は、水道のパイプに似ています。1つの蛇口(USBポート)から、たくさんのシャワーヘッド(センサーやモーター)に同時に水(電気)を送ろうとすると、1つあたりの水圧(電流)が弱くなってしまいます。電気をたくさん使う部品には、別の水道(外部電源)を用意してあげるのが安全です。
Chapter 07 / Multiple I2C Sensors

07応用:同じI2Cセンサーを複数つなぐには

OLEDディスプレイやジャイロセンサーなどで使われるI2C通信は、複数台つなぐ際に特有の注意点があります。

かんたん説明
I2C通信は、1本の線に複数の機器をつなぎ、それぞれの機器に割り振られた「名前(アドレス)」を呼びかけて会話する仕組みです。ところが、同じ種類のセンサーを2個買うと、工場出荷時のアドレスがまったく同じであることが多く、2個同時につなぐと「同じ名前の人が2人いて、呼びかけてもどちらが返事をしたかわからない」状態になってしまいます。

解決策①:アドレスを変更できるセンサーを使う

センサーによっては、基板上のジャンパー(はんだブリッジ)やピンの接続を変えることで、I2Cアドレスを切り替えられる機種があります。この場合は、配線の変更だけで複数台を見分けられるようになります。

解決策②:I2Cマルチプレクサ(TCA9548Aなど)を使う

アドレスを変更できないセンサーの場合は、「TCA9548A」のようなI2Cマルチプレクサ(複数の回線を切り替える部品)を使います。Arduinoから見ると、マルチプレクサの8つの「チャンネル」それぞれに、同じアドレスのセンサーを1個ずつつないでおき、プログラム側で「今はチャンネル0と話す」「次はチャンネル1と話す」と切り替えながら通信します。

#include <Wire.h>

#define TCA9548A_ADDR 0x70  // マルチプレクサ自体のアドレス(代表的な例)

void selectChannel(uint8_t channel) {
  Wire.beginTransmission(TCA9548A_ADDR);
  Wire.write(1 << channel); // 指定したチャンネルだけを有効にする
  Wire.endTransmission();
}

void setup() {
  Wire.begin();
  Serial.begin(9600);
}

void loop() {
  for (uint8_t ch = 0; ch < 2; ch++) {  // チャンネル0と1に同じセンサーがある例
    selectChannel(ch);
    // ここで、通常どおりそのセンサー用のライブラリ関数を呼び出す
    // 例:float temp = sensor.readTemperature();
    delay(10);
  }
}

なお、TCA9548Aのアドレス(0x70など)は製品や配線(A0〜A2ピンの接続)によって変わることがあるため、お使いの機種のデータシートで必ず確認してください。

Chapter 08 / Common Troubles

08よくあるトラブルQ&A

複数センサーを扱うときに、よく起きるトラブルをまとめました。

1センサーを増やしたら、全体の反応が遅くなった
① コード内にdelay()が複数箇所にないか確認する。
② あれば、5章で紹介したmillis()を使った書き方に置き換える。
③ pulseIn()を使う超音波センサーがある場合、タイムアウト値を短く設定し、待ち時間の上限を減らす。

2センサーを増やしたら、値が時々おかしくなる
① 配線の接触不良がないか、ブレッドボードを含めて確認する。
② 6章の内容を確認し、電源(電流)が足りているか見直す。
③ 特にモーターやサーボと同時に使っている場合、ノイズの影響でアナログ値が揺れることがあります。モーターの電源ラインを分離すると改善することがあります。

3配列のインデックスがずれて、違うセンサーの値が表示される
① sensorPins[]とsensorNames[]など、複数の配列を使っている場合、並び順が一致しているか確認する。
② センサーを1つ追加・削除したときに、関連するすべての配列を同時に修正したか確認する。
③ 可能であれば、ピン番号・名前・値をまとめた構造体(struct)を使うと、ズレを防ぎやすくなります。

Chapter 09 / Summary

09まとめ:複数センサー設計の基本方針

複数センサーを扱うときの5つのコツ

  • Arduinoの「同時」は、内部的には超高速な「順番処理」。analogReadは約100マイクロ秒、digitalReadはさらに高速
  • センサーが増えたら、個別の変数ではなく配列(array)でまとめて管理する
  • delay()は他の処理を止めてしまうため、複数センサーではmillis()による非ブロッキング制御が基本
  • pulseIn()など「待つ」命令を使うセンサーは、タイムアウト値を設定して影響を最小限にする
  • 電源は「センサーが増えるほど消費電流も増える」ことを意識し、必要なら外部電源を用意する

複数センサーをうまく扱えるようになると、Arduinoでできることの幅は一気に広がります。まずは2〜3個の簡単なセンサーから、配列とmillis()の組み合わせを練習してみてください。

関連記事・参考資料

基礎を学べる記事

応用・関連記事

参考資料(外部・一次情報)

  1. Gammon Software Solutions「ADC conversion on the Arduino (analogRead)」, https://www.gammon.com.au/adc
  2. Arduino公式リファレンス「analogRead()」, https://docs.arduino.cc/language-reference/en/functions/analog-io/analogRead/
  3. Arduino公式リファレンス「millis()」, https://docs.arduino.cc/language-reference/en/functions/time/millis/
  4. Random Nerd Tutorials「TCA9548A I2C Multiplexer: ESP32, ESP8266, Arduino」, https://randomnerdtutorials.com/tca9548a-i2c-multiplexer-esp32-esp8266-arduino/

※ 本記事のコード・数値は2026年10月時点の情報をもとに作成しています。センサーの型番や配線は製品により異なるため、実際の制作時は各センサーのデータシート・取扱説明書も併せてご確認ください。


関連記事

特集記事

コメント

この記事へのコメントはありません。

TOP
CLOSE