ここまで、Arduino上にあるアナログピンを用いた電圧計測(A0~A5まで最大6チャネル)、ADS1115を用いた電圧計測(単動で最大4チャネル×4個接続可)、MCP4725を用いた電圧出力(1チャネルのみ)をマスターしました。このチャネルの扱いを劇的に柔軟化するのがマルチプレクサー(multiplexer)モジュール、通称”mux”です。ここでは、CD74HC4067という非常に安価で、非常に役に立つチップを使ったブレークアウトモジュールを使います。これ一つで、出力も入力も16チャネル化できます。
原理は簡単です。Arduinoのデジタルピン(A0、A1といったように「A」がついているのがアナログピンで、それ以外の数字がついているのがデジタルピン:電圧が高い(HIGH)・低い(LOW)のどちらかのみを出力あるいは入力できるピンのことです)を4つ使って、それぞれHIGH(電圧を出す)・LOW(GNDレベル、つまり0Vに落とす)を組み合わせれば、CD74HC4067のS0~S3ピンに2^4=16通りの信号を送れます。これによって、C0~C15の端子のうちどれかとSIG端子がつながります。これを使えば、例えば16個のセンサーの出力線をC0~C15につないでおき、Arduinoから順次信号を送れば、順次、接続センサーを切り替えてSIG端子から値を読むことができます。このSIG端子を、例えばADS1115に接続すればいいのです。また、このマルチプレクサーは双方向なので、もう1つ使って、センサーへの印加も順次切り替えることができます。マルチプレクサーとADS1115の複数チャネルを組み合わせたり、マルチプレクサーとマルチプレクサーを組み合わせていけば、100チャネル超えも可能です。
ここでは、5V印加で、出力レンジが0-5Vのセンサーが16個(ch0~15)あるとし、0.2秒ごとにch0から順に印加してその出力を読んでいく例を考えます。センサーは、種類にもよりますが、通常は10mA以下の電流で稼働するので、ここではArduinoの5Vピンから印加します。図のように接続します。なお、センサーは印加・GND・出力の3線式とし、すべてのセンサーのGND線はArduinoと共通のGND(図の黒ワイヤー)につながっているものとします。

ここで、第8回目の差動入力用スケッチを元にした、以下のものを使って下さい。例によって、変更した部分を赤で示しています。
#include <SPI.h>
#include <SD.h>
#include <DS3232RTC.h> //http://github.com/JChristensen/DS3232RTC
#include <Time.h>
#include <TimeLib.h>
#include <Wire.h>
#include <Adafruit_ADS1015.h> //https://github.com/adafruit/Adafruit_ADS1X15
const int chipSelect = 10; // Arduino UNOでは10、Arduino MEGAでは53
#define AVERAGENUM 20 // 取得データの平均化に用いるサンプル数
//#define CFACTOR 0.18750 // GAIN_TWOTHIRDS: +/-6.144V range: Calibration factor (mV/bit) for ADS1115
#define CFACTOR 0.12500 // GAIN_ONE: +/-4.096V range: Calibration factor (mV/bit) for ADS1115
//#define CFACTOR 0.06250 // GAIN_TWO: +/-2.048V range: Calibration factor (mV/bit) for ADS1115
//#define CFACTOR 0.03125 // GAIN_FOUR: +/-1.024V range: Calibration factor (mV/bit) for ADS1115
//#define CFACTOR 0.01563 // GAIN_EIGHT: +/-/0.512V range: Calibration factor (mV/bit) for ADS1115
//#define CFACTOR 0.00781 // GAIN_SIXTEEN: +/-0.256V range: Calibration factor (mV/bit) for ADS1115
Adafruit_ADS1115 ads; // ADS1115のインスタンスを作成
const int s0 = 3; // マルチプレクサーを制御するためにC0と接続するピンは3
const int s1 = 4; // マルチプレクサーを制御するためにC1と接続するピンは4
const int s2 = 5; // マルチプレクサーを制御するためにC2と接続するピンは5
const int s3 = 6; // マルチプレクサーを制御するためにC3と接続するピンは6
void setup(void)
{
/* ----- Setting up serial communication with PC ------ */
/* ここでUSBを介してPCとシリアル通信を始める。9600はシリアル通信のボーレート */
Serial.begin(9600);
while (!Serial) {
; // wait for serial port to connect. Needed for native USB port only
//何らかの問題があってシリアルポートに接続できないときは、このループにトラップされる
}
/* ----- Initialisation of SD card ------ */
Serial.print("Initializing SD card...");
//see if the card is present and can be initialized:
if (!SD.begin(chipSelect)) {
Serial.println("Card failed, or not present");
// don't do anything more:
return;
}
Serial.println("card initialized.");
/* ----- Setting time based on RTC ----- */
setSyncProvider(RTC.get); // ここでRTCの時刻をもとにArduinoの時刻を合わせる
if(timeStatus() != timeSet)
Serial.println("Unable to sync with the RTC");
else
Serial.println("RTC has set the system time");
delay(1000);
/* ----- Setting of ADS1115 AD converter ----- */
ads.setGain(GAIN_ONE);
ads.begin();
/* ----- Preparation of multiplexer signal ----- */
pinMode(s0, OUTPUT);
pinMode(s1, OUTPUT);
pinMode(s2, OUTPUT);
pinMode(s3, OUTPUT);
}
void loop(void)
{
/* データの読み取り */
int cnt1=0;
int cnt2=0;
float read_from_ads;
float averaged_input[16];
for(cnt2=0;cnt2<16;cnt2++)
{
connectMux(cnt2); //ここでチャネルcnt2(=0~15)に接続
delay(500);
averaged_input[cnt2]=0;
for(cnt1=0;cnt1<AVERAGENUM;cnt1++) //AVERAGENUMの数だけ繰り返す
{
read_from_ads = float(ads.readADC_Differential_0_1()); //チャネル0から差動(Differential)入力
averaged_input[cnt2]=averaged_input[cnt2]+read_from_ads/AVERAGENUM; //読んだ値を足していく:AVERAGENUMで割ることで平均化している
}
averaged_input[cnt2]=averaged_input[cnt2]*CFACTOR; //校正係数を掛けて電圧とする
}
/* PCのシリアルモニタに表示 */
Serial.print(year());Serial.print("/");Serial.print(month());Serial.print("/");Serial.print(day());Serial.print(" ");
Serial.print(hour());Serial.print(":");Serial.print(minute());Serial.print(":");Serial.print(second());Serial.println("");
for(cnt2=0;cnt2<16;cnt2++)
{
Serial.print(averaged_input[cnt2]);
Serial.print(" mV, ");
}
Serial.println(); //改行
/* SDカードに書き込み */
File dataFile = SD.open("datalog.csv", FILE_WRITE);
if (dataFile)
{
dataFile.print(year());dataFile.print("/");dataFile.print(month());dataFile.print("/");dataFile.print(day());dataFile.print(" ");
dataFile.print(hour());dataFile.print(":");dataFile.print(minute());dataFile.print(":");dataFile.print(second());dataFile.print(" ");
for(cnt2=0;cnt2<16;cnt2++)
{
dataFile.print(averaged_input[cnt2]);
dataFile.print(" mV, ");
}
dataFile.println(); //改行
}
dataFile.close();
delay(2000); //2000ミリ秒=2秒の停止
}
/* Function to connect to a particular channel by multiplexer (mux) */
int connectMux(int channel)
{
int controlPin[] = {s0, s1, s2, s3};
int muxChannel[16][4]={
{0,0,0,0}, //channel 0
{1,0,0,0}, //channel 1
{0,1,0,0}, //channel 2
{1,1,0,0}, //channel 3
{0,0,1,0}, //channel 4
{1,0,1,0}, //channel 5
{0,1,1,0}, //channel 6
{1,1,1,0}, //channel 7
{0,0,0,1}, //channel 8
{1,0,0,1}, //channel 9
{0,1,0,1}, //channel 10
{1,1,0,1}, //channel 11
{0,0,1,1}, //channel 12
{1,0,1,1}, //channel 13
{0,1,1,1}, //channel 14
{1,1,1,1} //channel 15
};
//loop through the 4 sig
for(int i = 0; i < 4; i ++){
digitalWrite(controlPin[i], muxChannel[channel][i]);
}
}
スケッチの見通しを良くするため、setup()とloop()以外に、はじめて自身で定義する関数(connectMux(int channel))を使います(昔どこかから拾ってきたコードですが、どこからか忘れてしまいました、すみません)。C言語を使ったことのある人なら説明不要と思いますが、最後の部分のint connectMux(int channel) { }で関数と呼ぶ一連の手続きを定義しています。C言語と異なり、Arduino言語では関数を宣言する必要はないようです。カッコ()の中のint channelというのは、整数(integer)を引数として渡すと、それをこの関数内でchannelという変数として使うという意味です。このconnectMux関数は、たとえばconnectMux(3)とすれば、ピンs0、s1、s2、s3に1、1、0、0(つまりHIGH、HIGH、LOW、LOW)が送られ、sigピンとc3ピンがつながるようになります。loop()関数内のconnectMux(cnt2);という文で、cnt2を0から15までforループで増やすことで、順次sigピンとc0~c15ピンをつなげています。以下に留意してください。
・接続があまり瞬間的すぎるとセンサーが安定しない可能性があるので、delay(500);で各チャネル500msecずつ待っています。ほとんどのセンサーで、100msecくらいまで落としても問題ないと思います。
・デジタルピンは、出力に使うのか入力に使うのか指定する必要があります。setup()関数の中でpinModeという標準関数(自身で定義する必要がない関数)を使って定義しています。
・読んだ電圧値を格納するaveraged_inputは、16チャネル分の値を格納するためにaveraged_input[16]という配列として定義しました。
後の回で説明したいと思いますが、地盤工学でいえば、たとえばアナログ式土壌水分計(最近ブランドが変わりましたが、昔でいうDecagonのEC5など)やアナログ式圧力センサーなどを16個接続したいという時に便利です。しかも、順次印加するので、消費電流も限定的であり、大容量バッテリーや太いケーブルが不要です。