これまではモジュール化されて「接続するだけ」となったチップや変換器を紹介してきましたが、ここではセンサーそのものであるTE Connectivity社のMS5837-30BAという圧力センサーを紹介します。本体そのものが極めて小さいので、このセンサー本体の扱い方を知っていると、いろいろな場所に埋め込んだり、便利なことがあると思います。MS5837-30BAは容量3MPaの絶対圧センサーで、AD変換器が内蔵されており、出力(I2C通信)は24bitデジタルです。圧力に加え、同時に温度も計測することができます。購入数により単価は異なりますが、1個の場合、買う場所によって1000~3000円くらいです。リールで数百個買うと、単価は数百円まで下がることがあります。

 

 

小さッ!基盤部は辺3.3mm、円筒部は先端のフランジ径が2.64mm、根元の径が2.98mmです。3mm径の穴にちょうどはまります。

 

 詳しい諸元やデータシートはこちらのメーカーウェブサイトから手に入ります。

https://www.te.com/jpn-ja/product-CAT-BLPS0017.html

まだ本格的に使い始めてから数か月で、長期的なフィールド挙動を確認していないのですが、少なくとも短期間使ってみた感じとしては、非常にコストパフォーマンスに優れたセンサーのようです。ただ、基板実装用であり、非常に小さいので、手作業での実装(ケーブルへのはんだ付け)がとても難しく、この過程でかなりの確率で失敗して無駄にしてしまうことがあります。ひどいときは5個くらい連続で壊してしまいます。「きさま・・・1つのセンサーを作るのにいくら壊してきた?」と聞かれたら、「お前は自分が食ったパンの枚数をおぼえているのか?」と答えるしかありません。ここでは、失敗しづらい実装法も紹介します。

 

ブレークアウトモジュール

 このセンサーの概要を示すために、まず、既に実装された製品を試してみます。基板実装されたものはAmazonなどで購入できますが、1個6000円程度とかなり高価なうえに、基板のせいで組み込みの自由がきかないという問題があります。あくまで評価ボードとして用いることになります。

 

使い方は簡単で、I2C通信によるものなので、たとえば第22回のBME280/BMP280と同様にArduinoに接続すればよいです。そして、例によってフリーのライブラリを使います。

https://github.com/bluerobotics/BlueRobotics_MS5837_Library

ここでは、毎秒(delay(1000);)圧力と温度を計測してシリアルモニタに表示するスケッチを示します。上記ライブラリにあるサンプルとほぼ同じです。ちなみにこのセンサーのI2Cアドレスは0x76ですが、このライブラリのコードにすでに書き込んであるので、ユーザーがどこかに書く必要はありません。なお、MS5837-30BAの入力電圧は1.5-3.6Vなので、VDDにはArduinoの3.3V出力端子を接続します。このブレークアウトモジュールは電圧レギュレータが載っているので問題ありませんが、後述のように「素」から製作した場合、5Vにつなぐと簡単に壊れたので注意してください。

 

 



#include <Wire.h>

#include "MS5837.h"

 

MS5837 sensor;

 

void setup()

  Serial.begin(9600);

  Wire.begin();

 

  // Initialize pressure sensor

  sensor.init();

  delay(1000); 

 

  sensor.setModel(MS5837::MS5837_30BA);

  sensor.setFluidDensity(997); // kg/m^3 (freshwater, 1029 for seawater)

}

 

void loop()

{

  sensor.read();

  Serial.print(0.1*sensor.pressure());

  Serial.print(" kPa, ");

  Serial.print(sensor.temperature());

  Serial.println(" deg C");

 

  delay(1000);

}

 

 

 

センサー本体からの製作

 センサーそのものは、最初から小さな基板に載っています。この基板裏に4つの金属パッドがあり、これがI2C通信に必要なVDD、GND、SCL、SDAになります。このパッドに4芯ケーブルのそれぞれの芯をはんだ付けするのですが、注意点としては、

・VDD - GND間には100nFのコンデンサが必要

・VDD – SCL、VDD – SDA間には10kΩの抵抗が必要

・ケーブルはシールドケーブルがよい?

・パッドは非常にはがれやすく、一度はがれると復旧は無理

 

10kΩの抵抗についてですが、仮にマイコン(Arduino)側にプルアップ抵抗があっても、このセンサー直近に追加しないとうまく動作しませんでした。また、I2CはSPI同様、周辺機器との通信を目的としており、ケーブルが長くなるとうまく動作しないことがあります。シールドケーブルを用いることでノイズに強くなりますが、ケーブル長には限界があると思います。ここで紹介する方法で、3mまでは動作確認しています。では、一つ一つ解説していきます。

 

① ピンヘッダ―のはんだ付け

 ケーブルの先端をバラにして直接MS5837-30BAの基板裏にはんだ付けを何度も試みましたが、つけ終わった芯に力がかかるたびにパッドがはがれてしまい、5連敗ほどして、この方法は諦めました。これを避ける方法として、ピンヘッダーをあらかじめ4本ブロックにしておき(黒いプラスチック部分に瞬間接着剤をつけて固定する)、これをはんだ付けします(パッドにあらかじめはんだをつけておくといいです)。これに成功したら、勝負は8割決まったと思っていいです。なお、はんだ付けに手こずって基板を熱しすぎると、表面加工が融けてパッドを覆ってしまい、はんだが付かなくなります。

 

 

② コンデンサー・抵抗×2のはんだ付け

 下の図のようにコンデンサー・抵抗×2をつけます。写真の通り、とても小さいのでなかなか難しいです。特に、VDD – SDAという対角線の位置関係にあるピン同士を抵抗でつなぐには技術が必要です。ピンセットとツールクリッパーは必須です。

 

 

③ ここで、4芯シールドケーブルをはんだ付けします。ケーブルは、細いロボットケーブルなどが便利です。モノタロウで5mが1000円ほどで買えます。

 

 

④ 以上で、電気的な作業は終わりで、あとは筐体作製になります。この状態で放っておくとピンヘッダーとパッドの間に力がかかることがあるので、まずはその前にこの状態を固定します。速乾性アラルダイトで基板を覆って固定するといいです。アラルダイトは絶縁体なので、コンデンサー・抵抗を覆っても構いません。ところで、この圧力センサーを最終的にどのように対象物とコンタクトさせるかという問題があります。単に水位を測るだけならば、水中に放置するだけなので、先端はこのままでも構いません。しかし、例えば管の内部などの圧力を測る場合、チューブに接続しなければなりません。ここでは、外径4mmのポリウレタンチューブに接続することを想定し、ワンタッチタイプのハーフユニオンに接着します。SMC製のKQ2H04-01NSは孔の内径が3mmなので、MS5837-30BAの筐体がちょうどはまります。この状態でアラルダイトでくっつけると便利です。

 

 

 

⑤ これに、内径10mm弱のステンレス管をちょうどよい長さに切ってはめます。ステンレス管はホームセンターで買うか、100円ショップのガーデンライトの茎の部分を使ったりしています。錆びないステンレスがよいですが、ちょうどよい太さのものがあればプラスチックでもいいかもしれません。そして、両端を再びアラルダイトで固定します。

 

 

⑥ 以上で完全防水になりました。ただ、アラルダイトなどのエポキシ樹脂は水中で劣化します。これを抑えるために、さらに防水タイプ(内側にシール材が塗布されているもの)の熱収縮チューブを被せます。

 

 

これで非常にそれらしく見えます。ケーブルの先の4本を先の図のように接続すれば使えます。ノイズの問題があるようなら、シールドをGNDにつないでみます。

 

パフォーマンス

 上記のようなモデルを4個作って較正したところ、ゲージ圧0~80kPaに対してほぼ正確に反応しました。ただし、オフセット(大気圧開放下での出力値)には±1.5kPa程度の個体差がありました。個体ごとのオフセットは記録しておいたほうがいいでしょう。真空レギュレータを使って負圧に対しても較正しましたが、こちらも正確に反応しました。

 バッテリーでArduinoを駆動した際には出力は非常に安定しており、48時間の計測値の増減はAMeDASの気圧データと一致しました。ただし、携帯電話充電用の5V出力アダプタを使ってArduinoのUSB接続端子から給電したところ、値がかなりゆれました。3.3V出力端子から印加するときにArduinoからの電圧は安定化されているはずなので、この結果は不思議なのですが、とにかくこのようになりました。長期観測結果がおかしい場合は給電方法も検討してください。適切な方法で使用すれば、下図のような安定性が得られるはずです。

 

 

 最後に余談ですが・・・製作の過程でたくさんのMS5837-30BAを壊したと書きました。壊したら分解せずにはいられないのが理系の性ですが、分解すると、実はセンシング部分は本当に塵くらいの大きさしかないことがわかります。半導体テクノロジーの世界では当たり前なのでしょうが、どうしてこのようなものが作れるのか、土木系の自分は驚嘆するばかりです。大きいものを作るのも大変ですが、小さいものをつくるのは本当に技術だと思います。

 

 

 ここから2回、水圧計測用のセンサーを紹介します。間隙水圧計測は地盤工学のモニタリングの中でも最重要項目ですし、水工学でも河川やため池の水位を計測する必要があり、水圧センサーの役割は大きいです。本ブログでは、後の「実践編」にて、水圧センサーを用いたテンシオメーターの作り方を紹介します。センサーを選ぶ際に、容量(何kPaまで測れるのか)や精度も重要ですが、実用上、以下に特に気を付ける必要があります。

 

・絶対圧センサーか、ゲージ圧センサーか?

 絶対圧(大気圧変動の影響をうける)を計測するのか、ゲージ圧(大気圧からの差分)を計測するのかは重要です。多くの応用(例えば水位計測)で、大気圧変動による圧力の読みは取り除きたい(水位とは無関係なので)ものになりますが、前者の場合は別途、大気圧の同時計測も必要になります。ただし、ゲージ圧計は大気圧補償を行うために空気穴がある場合が多く、防水性が低い(あるいはない)などの欠点もあります。

・アナログ出力か、デジタル出力か?

 これは圧力センサーに限った話ではないですが、出力がアナログ方式(電圧あるいは電流)か、デジタル方式か(I2Cなどの通信プロトコルに基づく)により、データ取得に必要なロガーが異なるので注意が必要です。

 

 今回紹介する圧力センサーのSMC製PSE570はゲージ圧計でアナログ出力(電圧)です。最も「典型的」なセンサーともいえます。直流で印加して直流電圧でデータが帰ってくるので、扱い方は、原理的には第24回の土壌水分計と同じです。このような圧力センサーは、たとえばRS Componentsのウェブサイトなどで検索すれば何百種類もヒットするので、好きなものを使えばいいのですが、ここ3~4年、このようにしていろいろなセンサーを使ってきた経験を交えて今回書かせて頂きます。

 

 SMC製PSE570はリード線付きでモノタロウにて1万円くらいで売っており、容量は0~1MPaです。姉妹品でPSE573というものもあり、こちらは容量が-100~+100kPaです。後者は負のゲージ圧が測れるので、テンシオメーター(負の間隙水圧が測れる装置)に用いることができます。容量以外はどちらも同じで、印加電圧DC12~24Vで、出力はDC1~5Vです。接続口にR1/8あるいはR1/4おねじが選べます。

 

 

ここで、印加電圧DC12~24Vというのが問題です。試したところ、DC9Vくらいまで印加電圧を下げても応答しますが(9V以上だったら、出力は印加電圧に依存しません。内部に電圧レギュレータが入っているのでしょう)、Arduinoからの印加はせいぜい5Vです。ですので、印加用に別の電源が必要となります。

 

では接続方法について紹介します。基本的にここまでの回の知識を応用するだけで、新しいことは大してありません。

 

①単純に連続印加して電圧をArduinoのアナログピンで読む場合

 以下のように接続します。PSE570のリード線のスリーブの色はわかりづらいので注意が必要です。茶:印加黒:出力青:GNDです。直感とかなり異なります。また、12Vの電源として鉛シールバッテリーを使っています(ネット上のどこかから頂いてきたFritzingのイラスト部品ですが、35Ahってすごいな・・・フィールド観測で使うのはせいぜい12Ahくらいだと思います)。エネループ(1.3V)の9本直列でもいけるでしょう。5V系のArduino UNOを用いる場合、圧力計測の分解能は、1.22kPa(=1000kPa/(5-1)V × 5V/(2^10bit))になります。2秒に一度、出力電圧を読んでいます。

 

 



void setup(void)

{

    /* ----- Setting up serial communication with PC ------ */

    Serial.begin(9600);

    while (!Serial) {

    ; // wait for serial port to connect. Needed for native USB port only 

    }

}

 

void loop(void)

{

    float vinput;

 

    vinput=5000.0*analogRead(A0)/1023;

    Serial.print(vinput);

    Serial.println(" mV");

   

    delay(2000);

}

 

 

 

②必要なときのみ印加して電圧をArduinoのアナログピンで読む場合

第11回で学んだメカニカルリレー(GROVEリレー3.3V用)を使用します。リレーを開いて印加して1秒待ってから計測を行い、また次の計測時までリレーを9秒間閉じます。

 

 



const int RelayPin = 3; // リレーに信号を送るデジタルピン番号

 

void setup(void)

{

  /* ----- Setting up serial communication with PC ------ */

  Serial.begin(9600);

  while (!Serial) {

  ; // wait for serial port to connect. Needed for native USB port only 

  }

 

  pinMode(RelayPin, OUTPUT); // リレーに信号を送るデジタルピンを出力モードに設定

}

 

void loop(void)

{

  float vinput;

 

  digitalWrite(RelayPin, HIGH);

  delay(1000); 

  vinput=5000.0*analogRead(A0)/1023;

  Serial.print(vinput);

  Serial.println(" mV");

  digitalWrite(RelayPin, LOW);

 

  delay(9000);

}

 

 

 

③必要なときのみ印加して電圧をADS1115で読む場合

ADS1115については第8回を参照してください。ここでは、出力が1~5Vであることをふまえると、第8回に設定したADS1115のゲイン(利得)による計測範囲(4.096V)では不十分なので、ゲインを落として計測範囲を6.144Vまでとしています。分解能は0.023kPa(=1000kPa/(5-1)V × 6.144V/(2^16bit))となります。リレーの動作については②と同じことをやっています。なお、ここでは電圧を計20回読み込み、その平均値を表示するようにしています。これはADS1115で読むときに限らず、①②でももちろん同じことができます。

 

 



#include <Adafruit_ADS1015.h>    //https://github.com/adafruit/Adafruit_ADS1X15

 

#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(0x48); // 0x48はADDRをGNDにつないだ場合のI2Cアドレス

 

const int RelayPin = 3; // リレーに信号を送るデジタルピン番号

 

void setup(void)

{

    /* ----- Setting up serial communication with PC ------ */

    Serial.begin(9600);

    while (!Serial) {

    ; // wait for serial port to connect. Needed for native USB port only

    }

 

    /* ----- Setting of ADS1115 AD converter ----- */

    ads.setGain(GAIN_TWOTHIRDS);

    ads.begin();

 

    pinMode(RelayPin, OUTPUT);

}

 

void loop(void)

{

    int cnt1=0;

    float read_from_ads;

    float averaged_input;

 

    /* リレーの駆動 */

    digitalWrite(RelayPin, HIGH);

    delay(1000); 

 

    /* ADS1115からの読み込み */

    averaged_input=0;

    for(cnt1=0;cnt1<AVERAGENUM;cnt1++) //AVERAGENUMの数だけ繰り返す

    {

      read_from_ads = float(ads.readADC_Differential_0_1()); //チャネル0から単動(Single End)入力

      averaged_input=averaged_input+read_from_ads/AVERAGENUM; //読んだ値を足していく:AVERAGENUMで割ることで平均化している

    }

    averaged_input=averaged_input*CFACTOR; //校正係数を掛けて電圧とする

 

    /* PCのシリアルモニタに表示 */

    Serial.print(averaged_input);Serial.println(" mV");

 

    /* リレーをOFF */

    digitalWrite(RelayPin, LOW);

 

    delay(9000);

}

 

 

ちなみに、Arduinoアナログピン(平均化なし)とADS1115(20回平均)で出力を比べると以下のようになります。ここではPSE570ではなく、たまたま手元にあったPSE573(先に紹介した、計測範囲-100~+100kPaの連成計)を使いました。大気圧(ゲージ圧0kPa)を計測したので、出力レンジ1~5Vの中間の3Vくらいが出力されるはずです。

 

  Arduinoアナログピン(平均化なし)

 

  ADS1115(20回平均)

 

後者の場合、ざっと+/-0.5mVくらいでばらついているので、精度としては200kPa/4000mV×1mV=0.05kPa、水頭にして約5mmです。

 

水圧センサーの選び方についてさらに

 圧力センサーの耐久性や性能はまちまちです。Amazonで水圧センサーを検索すると、1000円程度のものから見つかります。これらの中には、「適用流体:空気・水」とあるのに、水に触れると数日で錆びるものもあります。また、RS Componentsなどで4000円程度のものを買っても、やはり長期的に錆びたりします。また、PSE570のように(たぶん)電圧レギュレータが内蔵されていない場合、印加電圧によって出力が変わるので、印加用バッテリーの残存容量に依存して出力がドリフトしていきます(自身で電圧低下回路を作らなければなりません)。私も、ケチをしたせいでだいぶ損をしました。秋田の堤防に自作テンシオメーターを設置する際、まず1000円の圧力センサーから使い始めましたが、すぐにダメになり、1泊2日の秋田出張で4000円のものに交換。これも半年でダメになり、結局また出張をしてSMC PSE573に交換。最初から1万円くらいは払っておけばよかった。連れていった学生の旅費も含め、20万円はかかりました。

 ではPSE570/PSE573の耐久性はどうか?室内ではまず問題ありません。問題になるのはフィールドのほうです。これはゲージ圧計のため、リード線を接続する箇所のあたりに筐体に開口部があります。これを完全に塞ぐと、ゲージ圧計と絶対圧計の中間の中途半端な挙動になるでしょうし、大気圧がうまく漏れる程度に簡易防水とするしかありません。仕方がないのでゆるくシールテープを巻きました。堤防中に10個ほど埋設し、2年の間に3つくらいは機能しなくなりました。水浸せずとも、地中(土に直接触れるのではなく、塩ビ管の中に入れているのですが)では結露が起こるので、やはり完全防水できないものは原理的に無理があります。メーカーさんも、現地計測用として売り出しているわけではないと思います。一方、上に書いたように室内試験用としてはコストパフォーマンスに優れたよいセンサーだと思います(SMCさんの空圧機器は性能・デザインともによくておススメです)。

 今回は、土壌水分計の出力の記録について説明します。ここではアナログ式、つまり出力が電圧で返ってくるタイプのものを扱います。土壌水分計の定番といえば、METER(旧Decagon Devices)のEC-5があります(http://www.ai-nex.co.jp/ECH2Oprobe-soil.html)。温度も同時に測れる5TM(現在、販売中止と書いてあります)などはSDIによるデジタル式なので、ここで紹介する方法では扱うことができません。

 

 

 実は、似たような土壌水分センサーはいろいろあり、Amazonで検索すると数百円~千円程度で多くのセンサーヘッドが見つかります。例えば、以下の土壌水分センサーは3つで880円。

 

 

このセンサーの詳細はわかりませんが、入力(印加)は3.3~5.5Vとのことです。電圧レギュレータ内蔵とのことなので、実際には3.3V以下で動いているのでしょう。今回はこれら2つを扱ってみますが、いずれも扱い方は基本的に同じです。

 

EC-5

 扱い方に特段難しいことはありませんが、連続して印加しないように、とマニュアルには注意書きがあります。どの程度の時間を連続と考えるのかよくわかりませんが、省電力の観点からも、記録をとる寸前に印加するのがよいと思います。励起(印加)電圧は2.5~3.6Vで、出力は約 0.22~0.8V、計測時間は10msとのことです。また、計測間隔を10秒以上とるように、との注意書きもあります。ただ、少なくとも短期間、毎秒計測しても特段の問題が起こったようには見えませんでした。ターミナルは写真のようにピンジャックあるいはバラ線となっています。私はピンジャックで届いても、切ってバラにして接続しています。ロットによってスリーブの色が異なるようで、

 

赤:Analog out(出力)、素線:Ground、白:Excitation(印加)

あるいは

オレンジ:Analog out(出力)、素線:Ground、茶:Excitation(印加)

 

にいずれかになります。

 

Arduino UNOのアナログ計測は第4回で説明した通り、5Vレンジの10bitなので(この5V、という範囲は実はREFピンを使って変えられます)、分解は約0.005Vで、EC-5の出力幅0.6V(=0.8-0.22V)を120段階にしか分解できないことになります。これが不十分なら、第8回で説明したようなADCモジュールを使います。

 

問題は印加のほうになります。Arduino UNOやArduino Nanoには3.3V出力ピンがありますが、このピンからは連続的に印加されるので、(マニュアルを真面目にうけとるなら)EC-5の印加には向きません。デジタルピンを使って印加すればON/OFFできるわけですが、これらのArduinoは5V系モデルなので、ONにするたびに5Vで印加されてしまいます。この問題を避けるためには、以下の3つの方法が考えられます。

① Arduino Pro Mini 3.3V 8MHzモデルのように、3.3V系のマイコンボードを使ってデジタルピンから印加する

② Arduino UNOなど5V系マイコンボードの3.3V出力ピンを使うが、第10回で紹介したマルチプレクサーモジュールを使って限られた時間のみ通電する

③ Arduino UNOなど5V系マイコンボードの5Vデジタルピンを使うが、電圧レギュレータを使って3.3Vに落としてから印加する

 

それぞれの接続例とスケッチを示します。せっかくですので、3つのEC-5を接続することを想定します。ここでは、「測定間隔を最低10秒とせよ」というのをガン無視して、2秒間隔(delay(2000);とある通り)としています。個人的経験としてこれで壊れたことはありませんが、各自の責任で行ってください。

 

① Arduino Pro Mini 3.3V 8MHzモデルのように、3.3V系のマイコンボードを使ってデジタルピンから印加する

 下の図のように接続しますが、ここはUSB-シリアル変換モジュール(図中の赤い基板)のVcc出力が3.3Vに落とされていることが前提です(PCのUSB端子からの印加は5Vです)。もしこの端子から5V出ている場合には、Arduino Pro Mini側のVccではなくRAWにつないで下さい。Arduino Pro Miniのボード上で3.3Vに落とされてから入力されます(詳しくは第12回参照)。印加開始して(digitalWriteでHIGHにして)から100msec待ち、電圧を読んだ後にdigitalWrite -> LOWで印加を停止しています。

 

 



#define CHANNELNUM 3 // Number of EC-5 connected to Arduino

 

const int apin[CHANNELNUM]={0, 1, 2}; // Analog pins to read the EC-5 outputs

const int dpin[CHANNELNUM]={2, 3, 4}; // Digital pins to energise EC-5

 

void setup(void)

{

    int cnt1;

 

    /* ----- Setting up serial communication with PC ------ */     

    Serial.begin(9600);

    while (!Serial) {

    ; // wait for serial port to connect. Needed for native USB port only

    }

   

    for(cnt1=0;cnt1<CHANNELNUM;cnt1++)

    {

      pinMode(dpin[cnt1], OUTPUT);

    } 

}

 

void loop(void)

{

    int cnt1; //general-purpose counter index

    float ec5_reading[CHANNELNUM];

   

    for(cnt1=0;cnt1<CHANNELNUM;cnt1++)

    {

      digitalWrite(dpin[cnt1], HIGH);

      delay(100);

      ec5_reading[cnt1]=analogRead(apin[cnt1])*3300.0/1023;

      digitalWrite(dpin[cnt1], LOW);

    }

 

    /* ----- Show the data on PC ----- */  

    for(cnt1=0;cnt1<CHANNELNUM;cnt1++)

    {

      Serial.print("Ch");Serial.print(cnt1);Serial.print(": ");Serial.print(ec5_reading[cnt1]);Serial.println(" mV");

    }

    Serial.println("");

     

    delay(2000);

}

 

 

最近、写真を見せていなかったので、久々に雰囲気を見せます。自宅にEC-5が1つしかなかったので、ここでは1つしかつないでいません。また、私が持っているUSB-シリアル変換モジュールやArduino Pro Miniは、上のFritzing図と比べると6線の端子が逆転(鏡面対称)になっていました。よくシルクを見て接続してください。

 

 

結果はこんな感じです。Ch0しかつなげていないので、Ch1・Ch2は不定値を示しています。244.38mVとありますが、センサー受感部を握ると、600mVくらいになります。

 

 

 

② Arduino UNOなど5V系マイコンボードの3.3V出力ピンを使うが、第10回で紹介したマルチプレクサーモジュール(図中の赤い基板)を使って限られた時間のみ通電する。

 簡単におさらいすると、s0~s3の4つのピンをつかって4bit(16通り)の信号を送ると、c0~c15のどれかとsigが双方向でつながるというスイッチング装置です。いずれのEC-5にも印加しない時間帯は、何もつないでいないC3とsigをつなぐようにスケッチを書きました。3.3V印加しているとはいえ、マイコン自体は5V系なので、電圧を変換するときは×3.3V/1023ではなく×5V/1023することに注意。

 

 

 



#define CHANNELNUM 3 // Number of EC-5 connected to Arduino

 

const int s0 = 3; // Used to control the multiplexer

const int s1 = 4; // Used to control the multiplexer

const int s2 = 5; // Used to control the multiplexer

const int s3 = 6; // Used to control the multiplexer

const int apin[CHANNELNUM]={0, 1, 2}; // Analog pins to read the EC-5 outputs

 

void setup(void)

{

    /* ----- Setting up serial communication with PC ------ */     

    Serial.begin(9600);

    while (!Serial) {

    ; // wait for serial port to connect. Needed for native USB port only

    }

 

    /* ----- Preparation of multiplexer signal ----- */

    pinMode(s0, OUTPUT);

    pinMode(s1, OUTPUT);

    pinMode(s2, OUTPUT);

    pinMode(s3, OUTPUT);

}

 

void loop(void)

{

    int cnt1; //general-purpose counter index

    float ec5_reading[CHANNELNUM];

   

    for(cnt1=0;cnt1<CHANNELNUM;cnt1++)

    {

      connectMux(cnt1);

      delay(100);

      ec5_reading[cnt1]=analogRead(apin[cnt1])*5000.0/1023;     

    }

    connectMux(cnt1+1);

 

    /* ----- Show the data on PC ----- */  

    for(cnt1=0;cnt1<CHANNELNUM;cnt1++)

    {

      Serial.print("Ch");Serial.print(cnt1);Serial.print(": ");Serial.print(ec5_reading[cnt1]);Serial.println(" mV");

    }

    Serial.println("");

   

    delay(2000);

}

 

/* 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]);

  }

}

 

 

 

 

③ Arduino UNOなど5V系マイコンボードの5Vデジタルピンを使うが、電圧レギュレータを使って3.3Vに落としてから印加する。

 ここで、電圧レギュレータというものについて説明します。(三端子)レギュレータは、下の写真のように端子が3つあり、真ん中がコモン(GND)で、入力側(左)に電圧を与えると、出力側(右)から一定の(入力値から下げられた)電圧が得られるものです。この写真は48M05Fで、5V出力タイプなので、入力には5Vよりいくらか大きい直流電圧を与えなければなりません。ただし、実際には入力・出力両方の端子に容量の異なるコンデンサーを接続するなどの手間が必要です。

 

 

これをすでにしてあるモジュールが安価に手に入るので、そちらを使うほうが簡単です。下の写真は3.3Vへ電圧降下を行うモジュールで、AMS1117というレギュレータが実装されています。Amazonで、HiLetGoさんが10個600円くらいで売っています。通電するとLEDが点灯するというサービス(?)付きです。

 

 

 

これを使えば・・・

 

 

スケッチ自体は①のケースとほとんど同じです。×3300.0/1023を×5000.0/1023に直した他、デジタルピンを番号を1つずらしただけです(特にずらした意味はありません)。

 



#define CHANNELNUM 3 // Number of EC-5 connected to Arduino

 

const int apin[CHANNELNUM]={0, 1, 2}; // Analog pins to read the EC-5 outputs

const int dpin[CHANNELNUM]={3, 4, 5}; // Digital pins to energise EC-5

 

void setup(void)

{

    int cnt1; //general-purpose counter index

   

    /* ----- Setting up serial communication with PC ------ */     

    Serial.begin(9600);

    while (!Serial) {

    ; // wait for serial port to connect. Needed for native USB port only

    }

 

    for(cnt1=0;cnt1<CHANNELNUM;cnt1++)

    {

      pinMode(dpin[cnt1], OUTPUT);

    } 

}

 

void loop(void)

{

    int cnt1; //general-purpose counter index

    float ec5_reading[CHANNELNUM];

   

    for(cnt1=0;cnt1<CHANNELNUM;cnt1++)

    {

      digitalWrite(dpin[cnt1], HIGH);

      delay(500);

      ec5_reading[cnt1]=analogRead(apin[cnt1])*5000.0/1023;

      digitalWrite(dpin[cnt1], LOW);

    }

 

    /* ----- Show the data on PC ----- */  

    for(cnt1=0;cnt1<CHANNELNUM;cnt1++)

    {

      Serial.print("Ch");Serial.print(cnt1);Serial.print(": ");Serial.print(ec5_reading[cnt1]);Serial.println(" mV");

    }

    Serial.println("");

 

    delay(2000);

}

 

 

 

 

3個880円の土壌水分計

 センサーに「Capacitive Soil Moisture Sensor(静電容量式土壌水分センサー)」なるジェネリックな名前しかついていないので、あえてこう呼ばせてもらいます。印加電圧はEC-5とほぼ同じなので、先の①~③接続図でEC-5をこのセンサーに単に置き換えればそのまま使えます。

 

 私が買ったものは先の写真のようにL型のピンヘッダ―がついてきました(白いコネクタ)。これにメスのジャンパーワイヤーをつなげばもちろんそのまま使えますが、防水にしたいので、はんだごてを使ってピンヘッダ―は取り除きました。そこに2芯のシールドケーブルをはんだ付けし、大径の熱収縮チューブで完全にシールしたのが下の図です。これで、不格好ながら、EC-5みたいな感じになりました。EC-5は16,000円ですが、これはケーブル2mと熱収縮チューブを含めて1000円しません。同じ性能があるかはわかりませんが・・・

 

 

両センサーの比較

 ここで、両センサーの応答を見てみます。長期的な比較をする時間がなかったので、10分くらいでできる簡単な比較だけです。Arduino Pro Mini 3.3V 8MHzに両センサーを1つずつ接続し、持ち運びやすいようにバッテリー駆動とし、結果がすぐ見えるようにLCD(第18回参照。ただし、ここでは20文字×4行ではなく16文字×2行を使っているので、I2Cアドレスなどに変更があります)表示としました。先の①~③の方法のうち、ここでは③を使いました(LCDは5Vで印加しないと明るさが足りないので)。第18回をとばしていて、まだLCDのライブラリをインストールしていない人は、それが必要なことに留意してください。

 

 

 



#include <LiquidCrystal_I2C.h>

 

LiquidCrystal_I2C lcd(0x3F,20,4);

 

const int s0 = 3; // Used to control the multiplexer

const int s1 = 4; // Used to control the multiplexer

const int s2 = 5; // Used to control the multiplexer

const int s3 = 6; // Used to control the multiplexer

 

void setup(void)

{

    Serial.begin(9600);

    while (!Serial) {

    ; // wait for serial port to connect. Needed for native USB port only

    }

 

    /* ----- Preparation of multiplexer signal ----- */

    pinMode(s0, OUTPUT);

    pinMode(s1, OUTPUT);

    pinMode(s2, OUTPUT);

    pinMode(s3, OUTPUT);

 

    /* ----- Initialisation of LCD ----- */

    lcd.init();

    lcd.backlight();

}

 

void loop(void)

{

  float ec5_reading;

  float sms_reading;

 

  connectMux(0);

  delay(100);

  ec5_reading=analogRead(A0)*5000.0/1023;

  connectMux(1);

  delay(100); 

  sms_reading=analogRead(A1)*5000.0/1023; 

  connectMux(2); 

 

  lcd.clear();

   

  lcd.setCursor(0, 0);lcd.print(ec5_reading);

  lcd.setCursor(0, 1);lcd.print(sms_reading);

  lcd.setCursor(4, 0);lcd.print(" mV");

  lcd.setCursor(4, 1);lcd.print(" mV");

 

  delay(2000);

}

 

/* 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]);

  }

}

 

 

 

 

 

空気中、水中に置いたり、我が家の軒先でペチュニアの鉢にさしてみました。

 

 

結果は、

 

 

 EC-5

 3個880円の土壌水分計

 空気中

 263mV

 2834mV

 乾いた鉢

 449mV

 2512mV

 水をあげたばかりの鉢

 630mV

 1661mV

 水中

 909mV

 1510mV

 

水を鉢の土に均一にあげられたわけではないので、これを見て線形性云々の議論はできませんが、安いセンサーも、出力レンジが広そうで、これだけ見れば悪くはなさそうです。少なくとも、地盤中のある位置が浸潤したかどうかを判断するくらいには使えそうです。

 

最後に

 ここで扱った土壌水分計はアナログ出力なので、扱いは簡単ですが、一方で気をつけないといけないこともあります。まず、印加電圧が安定していなければなりません。他にいろいろ周辺機器をArduinoにつなぎ、電力供給が足りないことによって電圧降下などが起きないようしてください。また、供給の問題かADC(アナログ-デジタル変換)の問題かわかりませんが、長期計測をしていると、全てのセンサーの値が一様にシフトすることなどあります。地盤の含水率がいたるところで急に上がった!と解釈するかもしれませんが、電圧がどこかで(入力か出力か)でシフトした可能性もあります。ですので、長期計測を行う際は、少なくとも1つ、ダミーとしてセンサーを余分に接続し、地盤に挿さずにロガーボックス中などに浮かせておくことをお勧めします。そうすれば、本当の地盤状態の変化と、電圧系のシフト(ドリフト)を区別することができます。

 

 

 

 今回は距離計測について書きます。地盤工学において変位計測は重要ですが、長期にわたり高精度を求めるならワイヤー式の変位計などを用いることになるでしょう。ひずみゲージ式のセンサーについては、第19回の方法でHX711などを用いてデータ取得が可能です。ここでは、非接触型のセンサーである赤外線ToF(Time-of-Flight)測距計と超音波測距計について紹介します。いずれも、ロボットの障害物回避などに用いられるもので、0.1mmなどといった精度を持つものではありません。しかし、後に示すようにそれなりの用途もあります。

 

赤外線ToFセンサー

VL53L0Xというセンサーです。ブレークアウトモジュールとして1000~1500円くらいを相場として売っています。このICはもはやおなじみのI2Cでマイコンに接続します。

 

 

写真の上にある黒いチップから光が出て、反射時間に基づいて距離を測るようです(この原理はよくわかりません。光の速度を直接測れるとは思えないので、干渉の原理などを利用しているのでしょうか)。それほど強い光ではないものの、直接覗き込まないほうがいいそうです。

 

 接続は以下のようにします。通信プロトコルはI2Cですので、もはやおなじみ、Vcc・GND・A4 (SDA)・A5 (SCL)の4本線で接続します。データシート(https://www.pololu.com/file/0J1187/VL53L0X.pdf)には動作電圧は「2.6 to 3.5 V」とあります。ほとんどのICに言えることですが、ICそのものは3.3V系ですが、ブレークアウトモジュールに電圧レギュレータがついていて、Vccを5Vに接続しても動作します。

 

 

 

以下がスケッチです。例によってライブラリをダウンロードしてください。

https://github.com/pololu/vl53l0x-arduino

VL53L0Xには4つの計測モードが用意されています。データシートによれば、以下の表の通りです。それぞれのモードを使う文をスケッチに残しながらコメントアウトして無効化してあるので、適宜コメントをはずして試してみて下さい。なお、スケッチは上記のライブラリにあるサンプルSingle.inoに基づいています(コメントを多少簡単にしてあります)。サンプルから解釈する限り、DefaultとLong rangeのどちらかとHigh accuracyとHigh speedのどちらかは組み合わせられる(4つのモードが個々に排他的ではない)ようです。距離はmmで返ってきます。

 

 Mode

 計測時間 (msec)

 計測範囲 (m)

 備考

 Default

 (既定)

 30

 1.2

 Standard

 High accuracy

 (高精度)

 200

 1.2

 Precise measurement

 Long range

 (広範囲)

 33

 2

 Long range, only for

 dark conditions

 High speed

 (高速)

 20

 1.2

 High speed where 

 accuracy is not priority

 

どの程度の精度があるのか?については、データシート(上記URL)のp.25~26に図があります。

 



#include <Wire.h>

#include <VL53L0X.h>

 

VL53L0X sensor;

 

// Uncomment this line to use long range mode.

//#define LONG_RANGE

 

// Uncomment ONE of these two lines to get higher speed at the cost of lower accuracy OR

// higher accuracy at the cost of lower speed

//#define HIGH_SPEED

//#define HIGH_ACCURACY

 

void setup()

{

  Serial.begin(9600);

  Wire.begin();

 

  sensor.init();

  sensor.setTimeout(500);

 

#if defined LONG_RANGE

  // lower the return signal rate limit (default is 0.25 MCPS)

  sensor.setSignalRateLimit(0.1);

  // increase laser pulse periods (defaults are 14 and 10 PCLKs)

  sensor.setVcselPulsePeriod(VL53L0X::VcselPeriodPreRange, 18);

  sensor.setVcselPulsePeriod(VL53L0X::VcselPeriodFinalRange, 14);

#endif

 

#if defined HIGH_SPEED

  // reduce timing budget to 20 ms (default is about 33 ms)

  sensor.setMeasurementTimingBudget(20000);

#elif defined HIGH_ACCURACY

  // increase timing budget to 200 ms

  sensor.setMeasurementTimingBudget(200000);

#endif

}

 

void loop()

{

  Serial.print(sensor.readRangeSingleMillimeters());

  if (sensor.timeoutOccurred()) { Serial.print(" TIMEOUT"); }

  Serial.println();

}

 

 

(参考)

 上記のスケッチ中における#if defined XXXというのは、XXXが定義されていれば、その次の行グループをコンパイルするという意味です(これや#defineなどはコンパイル後のコマンドになるというよりもコンパイラへの指示であり、ディレクティブと呼ばれます)。先に書いたように、コメントアウトを外せば、#if definedがtrueになるので、その次の文がコンパイルされ、実行可能になります。

 

超音波センサー

 障害物回避のためのセンサーとして定番で、しかも安価なため(数百円程度)、Arduino初修セットにだいたいついてきます。Amazonなどで検索すると、見た目はほとんど同じながら品番が異なるものがいくつかあります。この品番は何を表すものなのかよくわかりません。ICチップそのものの品番なのか、それとも振動子(スピーカーのように見える発信機(T: Transmitter)と受信機(R: Receiver))まで含めたシステムとしてのモジュールの品番なのか不明です。HC-SR04・US-015・HY-SRF05などが検索にかかると思います。HC-SF04が安くて流通しているのですが、一部のロットにバグがあるそうで、動作が「ハマって」計測が止まってしまうことがあるそうです。どれも使い方は全く同じですが、計測範囲(4m程度)や精度に多少の相違があるようです。また、周波数はいずれも40kHzのようです。

 

 

 

原理としては反射波の到達時間を計測するものですが、気温によって音波速度が異なるため、厳密にはその補正を行う必要があります。音波速度は気温o0Cを参照として、1oC増加するごとに0.18%大きくなります。0oCと20oCでは、速度が約3.6%異なり、補正しなければこれがそのまま測距の誤差となります。接続とスケッチは以下の通りです。ここでは音速を単に340m/secとして、反射波到達までの時間(μsec)に340000mm/secを掛けて(対象物までの距離を求めるのでさらに2で割って)、mmで距離を出しています。珍しく、どのようなライブラリも使いません。Arduinoの標準関数でパルス到達時間を計っています。

 

 

 



/* Sonic displacement sensors */

int inputPin=3; // pin to read signal from sensor

int outputPin=4; // pin to send signal to sensor

 

void setup(void)

{  

    int cnt1;

     

    pinMode(inputPin, INPUT);

    pinMode(outputPin, OUTPUT);

 

    Serial.begin(9600);

    while (!Serial) {

    ; // wait for serial port to connect. Needed for native USB port only

    }

}

 

void loop(void)

{

    /* Obtain distance from sonic sensor */

    int arriv_time;

    int distance;

    digitalWrite(outputPin, LOW);

    delayMicroseconds(2);

    digitalWrite(outputPin, HIGH);  

    delayMicroseconds(10);

    digitalWrite(outputPin, LOW); 

    arriv_time=pulseIn(inputPin, HIGH);

    distance=arriv_time*340000/1000000/2;

    delay(100);

 

    /* Display in the PC */

    Serial.println(distance);

}

 

 

 

適用例

 第21回に続いて、積雪深センサーとしての適用を紹介します。第21回で、温度計アレーの柱の上におかしなヘッドがついていましたが、ここから地表に向けて、US-015(超音波)・HY-SRF05(超音波)VL53L0X(赤外線ToF)の3つで距離を測り、ヘッド高さから差し引いて積雪深を北海道の黒松内という場所で一冬の間、記録しました。VL53L0XはLong rangeとHigh accuracyモードを設定しました。斜面に垂直に向けており、以下に示しているのはこの法線方向の深度(鉛直方向ではない)です。

 

 

これらのセンサーは頻繁にデータ欠損が起こるので、記録時にはそれぞれ5回の計測時を記録して、全てプロットしました。5回の記録中に正常値が残らないこともあります。正常値と思われるものが記録されるまで繰り返すなど、Arduinoのスケッチ側の工夫が必要と思います。このサイトではこの他にインターバルカメラや第21回で紹介したDS18B20などで積雪深を測っており、これらと概ね整合する値が得られています。グラフの内容をまとめると、

 

・積雪期の読みはVL53L0X(赤外線ToF)が最も安定しているが、春に0mmに戻っていない。地表の植生を通り抜けていないよう。

・5月以降はかなり草が伸びてきて、いずれのセンサーもこれに反応している。

・グラフでは見づらいが、US-015は欠損値が多く、連続的なデータを得られていない。HY-SRF05のデータはより連続的だが、1-2月の極端な増減は現実的でない(カメラからも確認済み)。

 

今のところ、いずれも高価ではないので、どっちもとりあえず載せとけ、という感じで使っています。市販の超音波積雪深計も40kHzを用いているようですが、このように欠損が多いのか、アルゴリズムで工夫しているのか、それとも印加電圧が異なるのか、わかりません。

 

 今週は第55回地盤工学研究発表会が京都で行われるはずでしたが、コロナウィルスのため中止になり、一部のセッションのみオンライン(Zoom)で実施されました。主に水害に関するいくつかのセッションを拝聴しましたが、やはり水位観測システムがン十万円という発表がいくつかあり、観測の価格破壊にはまだまだ余地があると感じました。このブログも急いで応用編に入って、より実用的な作例を紹介したいと感じました。実際の製品には人件費等が含まれるのでセンサーの価格の総和とはなりませんが、少なくとも研究は自作により廉価な装置を使えるはずです。

 

 前回の続きです。温度を計る別の方法として、Bosch Sensortec社のBME280あるいはBMP280というセンサーがあります。BMP280は温度・気圧(絶対圧)を計ることができ、その上位製品であるBME280は加えて湿度も計ることができます。センサーそのものは下の写真の通り非常に小さく(写真右の中央に見える約1mm×2mmの金属片)、このように基板実装したモジュールを使うことがほとんどだと思います。

 

 

BMP280はAmazonで最安300円くらい、BME280は800円くらいです。どちらも使い方はほとんど同じです。裏面にBMP/Eと書いてあり、印をつける白い□があります。ここに自分で印をつけておかないと(買うとすでに書いてある場合もあります)、見た目では区別がつかないので、わからなくなります。I2CでもSPIでも通信することができますが、モジュールによってはI2Cのポートしか引き出していなかったり、SparkFun社の製品のように、どちらもポートが引き出してあったりします。

 

 I2Cなので使いやすく、温度も気圧もそこそこ正確に測ってくれるので、かなりメジャーなセンサーであり、電子工作のウェブサイトや入門書を買うと、よく例として使い方が載っています。また、多くのマイコンボードやIDEに対してライブラリが提供されています(Raspberry PieとかESP32とか)。ただ、私の経験でいえば、地盤モニタリングではそれほど主役にはなりません。防水ではないのでロガーの中に入れることになりますが、計っているのは大気温度・湿度ではなくロガー内温度と湿度になるので、あまり大した意味は持ちません。気圧は正しく計れますが、近くのAMeDASステーションの大気圧で事足ります(リアルタイムで気圧を計りたい場合を除く)。湿度はそもそも正確に計れているのか評価するのが難しいです。このような理由で、ロガー内のおまけとして使うことが多いです。いずれにしても使い方を紹介します。ちなみに、英語が苦にならなければ、SparkFunのウェブサイトのほうが詳しいです。

https://learn.sparkfun.com/tutorials/sparkfun-bme280-breakout-hookup-guide

 

 ここではI2CでArduinoに接続することにします。I2Cアドレスは異なる2つの設定が可能で、SDOをGNDにつなぐと0x76、Vccにつなぐと0x77になります。他のI2Cセンサーと重複しなければどちらでもいいのですが、ここはVccにつないで0x77とします。これは、これから使うライブラリのヘッダーファイルAdafruit_BMP280.hの中にデフォルトで以下のようなコードがあるからです。

    #define BMP280_ADDRESS                (0x77)

アドレスを0x76として使う場合は、ここを書き変えて保存しなければなりません。

 

 

 スケッチは以下のようにします。ここで、ライブラリAdafruit_BMP280.hを使います。

https://github.com/adafruit/Adafruit_BMP280_Library

Adafruit_BME280.hはこちら

https://github.com/adafruit/Adafruit_BME280_Library

 

 



#include <Adafruit_BMP280.h>

 

Adafruit_BMP280 bmp;

 

void setup(void)

{   

    Serial.begin(9600);

    while (!Serial) {

    ; // wait for serial port to connect. Needed for native USB port only

    }

 

    /* ----- Starting up BMP280 ----- */

    if (!bmp.begin()) { 

      Serial.println(F("Could not find a valid BMP280 sensor, check wiring!"));

      while (1);

    }

}

 

void loop(void)

{

    Serial.print("Temperature = ");

    Serial.print(bmp.readTemperature());

    Serial.println(" *C");

   

    Serial.print("Atm. Pressure = ");

    Serial.print(bmp.readPressure());

    Serial.println(" Pa");

 

  delay(1000);

}

 

 

BME280を使う場合は、(ライブラリをダウンロードしたうえで)上のスケッチのBMPを全てBMEにすればいいです。これでシリアルモニタを見れば、毎秒、温度と気圧が表示されます。久しぶりに画面をキャプチャしました。

 

 

 下の図は、BMP280で計測した気圧とAMeDAS秋田の気圧を1か月にわたって比較したものです(AMeDASでは、気圧は管区のステーションでしか計測されていません)。オフセットがありますが、変化率は同じで、オフセット補正をすればAMeDASと等価なデータがとれることがわかります。これは例えば地下水位計測を目的として絶対圧センサーを地下水位以下に設置し、計測した水圧をゲージ圧(水深に相当)に変換するときなどに使えます。

 

 

 今回は、温度センサーDS18B20を紹介します。データシートによると、「… digital thermometer provides 9-bit to 12-bit」とだけあり、計測原理などについての言及はないようです。おそらくICセンサーかと思われます。(ワンチップ)ICセンサーとは「センシング入門」(オーム社)によると、「トランジスタのベース-エミッタ間電圧が、温度に比例して変化することを利用している・・・大量生産されるために安価であり、小型・高精度で経時変化も少ないのが特長である」だそうです。実際、このセンサーは非常に安く、同じ製品が様々な価格で売られているのですが、Amazonでは例えば2m程度のケーブル付きで、センシング部が金属の防水ケースに入ったものが10本3000円程度で売られています。ケーブルの価格だけでこのくらいするんじゃないかという価格です。DS18B20で検索して下さい。データシートはこちら。

https://datasheets.maximintegrated.com/en/ds/DS18B20.pdf

 

 

一度、壊れたものを鉄ノコで切断して中身を覗いたことがあります。こんなに小さいのに、なかなか切断に苦労しました。ケースはクロムメッキされた鉄でできているように見えました。ちょっと踏んだくらいでは壊れない、頑丈なつくりです。ケーブルなしで、センサーそのものだけでも売っています(下の写真で、カッターマットのグリッドは5mmです)。

 

 

この温度センサーはデジタルで、印加電圧(VDD・GND)に加えて1本の通信線を使用します。I2CでもSPIでもないということです(第5回参照)。データシートにある主な特長をまとめます。

・計測温度範囲:-55oC to +125oC

・正確度:±0.5oC正確性(-10oC to +85oC)

・プログラムにより分解能を9~12 Bitで変更可

 

DS18B20の使用方法

 このセンサーの優れた点は、いくら芋づる式に多連にして使っても1つのピンしか通信に使わないということです。I2Cに似て、個々のセンサーがそれぞれの固有の64-bitアドレスを有しています。ところがこのアドレスは購入したときの袋などには一切書いていないので、Arduino(などのマイコン)を使って読み出してやらなければなりません。このときは、1つずつ繋いで読み取っていきます。まずは以下のように一本ずつつないでください。DQのピンはどこでもいいです。スケッチで指定してください。ここではデジタルピン8を使っています。

注意点:

・ケーブルは何色が何、などという説明はついていません。赤・黒・黄でくることが多いですが、常識に沿って、赤:VDD、黒:GNDで、残った黄が通信線(データシートではDQと書かれています)になります。

・単につないだだけだとうまく通信できないことがあります。この場合、図のようにDQとVDDの間に1kΩ程度のプルアップ抵抗を入れてください。データシートの例では4.7kΩとありますが、私の経験では、Arduinoを使った場合、もう少し小さい抵抗でないと応答しませんでした。

 

 

次に、以下のスケッチを走らせます(どこかから拝借してきたものです。どこが元か忘れてしまいました。作った人、クレジットできずすみません)。以下のライブラリが必要になります。

OneWire.h

https://github.com/PaulStoffregen/OneWire

 



#include <OneWire.h>

 

OneWire ds(8);  // This is where DQ of your DS18B20 will connect.

 

void setup(void) {

  Serial.begin(9600);

  getDeviceAddress();

}

 

void getDeviceAddress(void) {

  byte i;

  byte addr[8];

 

  Serial.println("Getting the address...\n\r");

  /* initiate a search for the OneWire object we created and read its value into

  addr array we declared above*/

 

  while(ds.search(addr)) {

    Serial.print("The address is:\t");

    //read each byte in the address array

    for( i = 0; i < 8; i++) {

      Serial.print("0x");

      if (addr[i] < 16) {

        Serial.print('0');

      }

      // print each byte in the address array in hex format

      Serial.print(addr[i], HEX);

      if (i < 7) {

        Serial.print(", ");

      }

    }

    // a check to make sure that what we read is correct.

    if ( OneWire::crc8( addr, 7) != addr[7]) {

        Serial.print("CRC is not valid!\n");

        return;

    }

  }

  ds.reset_search();

  return;

}

 

void loop(void) {

  // do nothing

}

 

 

そしてArduino IDEのシリアルモニタを立ち上げると、センサーのアドレスが表示されます。これを何かにひかえてください。40代前後の人はドラクエIIの復活の呪文を思い出すかもしれません。書き間違えると悲劇が待っているやつです。

 

 次に、DS18B20で温度を測る場合です。接続は先ほどの図のままでいいのですが、ここでは2つのDS18B20をつないだ例を示します。以下のスケッチでは、前記のOneWire.hに加えて以下のライブラリが必要になります。

DallasTemperature.h

https://github.com/milesburton/Arduino-Temperature-Control-Library

 

 

一つコツなのですが、delay(800)やdelay(200)とあるように、センサーとの通信の間に少し時間を置く必要がある場合があります。説明通りにつないだのに動かない、という場合は、接続を確認するのは当然のこと、その他にあらためて、

・プルアップ抵抗を使っているか(使っていてても、抵抗値を変えてみる)

・センサーとの通信の間に時間をとっているか

・アドレスを書き間違えていないか

を確認してください。

 



#include <OneWire.h>

#include <DallasTemperature.h>

 

#define ONE_WIRE_BUS 8 // データ(黄)で使用するポート番号

#define SENSER_BIT    9      // 精度の設定bit

 

OneWire oneWire(ONE_WIRE_BUS);

DallasTemperature sensors(&oneWire);

 

DeviceAddress temp0 = { 0x28,0xF7,0xD8,0x26,0x00,0x00,0x80,0x7C }; //1つ目のDS18B20のアドレス

DeviceAddress temp1 = { 0x28,0xFF,0xDC,0x12,0x86,0x16,0x04,0x9C }; //2つ目のDS18B20のアドレス

 

void setup(void)

{   

    Serial.begin(9600);

    while (!Serial) {

    ; // wait for serial port to connect. Needed for native USB port only

    }

 

    sensors.setResolution(SENSER_BIT);

 

    delay(1000);

}

 

void loop(void)

{

    sensors.requestTemperatures();              // 温度取得要求

    delay(800);

    Serial.println(sensors.getTempC(temp0)); //1つ目のDS18B20から温度の取得、シリアルモニタに表示

    delay(200);

    Serial.println(sensors.getTempC(temp1)); //2つ目のDS18B20から温度の取得、シリアルモニタに表示

    delay(1000);}

 

 

実用例

 実用例として、積雪深ゲージを示します。寒冷地では融雪による土構造物の不安定化が懸念されることがあり、積雪深は地盤工学的にも興味があるところです。私が思いつく限り、積雪深を測る方法はいくつかありますが(Nishimura et al., 2000)、温度センサーのアレー(例えば10cmごと)を用意しておくというのは中でも利点の多い方法です。温度センサーが雪に埋まると、雪が断熱材となり気温の日変化に対して鈍感になるので、温度の変化に着目すると、どのセンサーまで雪に埋まったかすぐにわかります。インターバルカメラによる直接観察は、レンズに降雪が付着したり、電池寿命に限界があったりしますし、写真から積雪深を読み取る作業が必要です。データが大きく、無線遠隔転送にも向きません。超音波センサーなどによる測距は、上から地表にセンサーを向けるため、そこそこ大がかりなやぐらが必要になりますし、雪の表面が柔らかいと反射をよくピックアップできないこともあります。この温度センサーアレー方式は、積雪深分解能がセンサー間隔で決まるという限界がありますが、これに目をつむれば、なかなか優れたやり方に思えます。オマケとして、雪の温度も測れます(そんないい加減な測り方をしてはいけないとA川先生に怒られそうですが)。塩ビ管に穴をあけてDS18B20を埋め込むやり方なら、Arduinoと電池も含めて5000円くらいで製作できます。ちなみに下の図では、てっぺんにもいろいろ搭載されていますが、これは後の回で説明します。

 

 

 ここから3回、温度計測方法について紹介します。温度を測るには、当然、温度計が必要ですが、これには様々な原理に基づいた様々な種類のものがあります。中には原理をあまり明確に示していない製品もあります。自身の用途にどれが適しているかを検討しないといけないわけですが、精度やレンジという問題のほかに、形状や大きさ、防水性なども勘案しなければなりません。ここから3種類の温度センサーを順次紹介します。最初は熱電対(thermocouple)です。

 

 熱電対そのものについては別途検索して下さい。簡単に書くと、2種類の金属線が(互いに絶縁されて)1本に並んだ、2芯ケーブルです。異なる金属が接触すると、温度に応じた起電力が発生する(ゼーベック効果)という原理に基づいたものです。金属の種類によって、K型、T型、などいくつも種類があり、計測可能な温度範囲が異なります。この起電力は非常に小さいので、センサーとして用いるためには増幅してやる必要があります。土質試験では特段に高い温度を計測することは少ないと思います。最も一般的と思われるのがK型(クロメル―アルメル、-200~+1000℃:温度範囲はWikipediaによる)です。私は凍結問題を扱うので、むしろ低温側での計測が必要となりますが、T型(銅-コンスタンタン、-200~+300℃:同上)がよいと聞いています。ただ、使った印象としてはK型とそれほど変わらないと思いました。

 

 熱電対はAmazonでもMonotaroでもどこでも買えますが、凍土仲間のT先生に教えてもらったチノーという会社の「被覆熱電対」という製品を100m巻きで買っています(100mで確か2~3万円、そんなに長く必要なければ他でもっと安く買えます)。なお、この会社のウェブサイトの仕様ではK型・T型の使用温度上限は100℃となっています。金属の性質上+1000℃まで測れても、シース(絶縁体)が溶けては使えないですからね。

 

 熱電対は、先端をむき出して、2つの金属が触れるようによじってやればこれで完成です(右写真)。ほどけないように、また防水処理として、この後、エポキシ樹脂で先端を固めて保護します。この部分の温度が起電力(電圧)として線の反対側(左写真)で計測されます。なお、電圧は相対的なものなので、零点補償といって、氷水(0℃)を使って温度を較正するのが正確な使い方と言われていますが、市販のロガーでは、線をつなぐだけで零点補償をしてくれるものが多いです。詳しいことはよく知りませんが、おそらくロガー内部のサーミスタなどでロガー側の接点温度を計測し、それに基づいて補償するのだと思います。よくわからん?説明しているサイトがいろいろあるので検索してみてください。

 

 

 

 熱電対をArduinoに直接接続しても電圧が小さすぎて読み取れないので、例によってモジュールを紹介します。熱電対アンプモジュールはいくつかあります。簡単に手に入るものとしては、AD8495、MAX6675、MAX31855、MAX31856などがあります。AD8495は増幅した電圧をアナログで出力するもので、K型熱電対に対して約5 mV/℃で出力するそうです。Arduino UNOを用いる場合、電圧計測は5V/10bit=5mVなので、およそ1℃単位でしか計れませんね(第4回参照)。ADCモジュールADS1115(第8回参照)を使えれば、4V/16bitなので0.01~0.02℃くらいの分解能で読めそうです。実は、持っているのですが、使ったことがありません。次に紹介するMAX31856を使うことが多いからです。

 

熱電対モジュールMAX31856

 

 MAX31856はMAX6675やMAX31855の上位版チップのようですが、あるサイトによれば、性能は各段に違うようです。価格も違いますが、研究用なら間違いなくMAX31856のほうがよいとのこと(MAX31855も使ったことがないのでわかりませんが)。ここではMAX31856の使い方を紹介します。MAX31856を搭載したモジュールはやや高めで、1500~2000円くらいで売っていることが多いです。Adafruit製が「正規品」のようですが、同じ作りのものは例によってたくさん売っています。2極端子台がついてくることが多く、写真中右のようにはんだ付けすると便利です。対応している熱電対の型は、B, E, J, K, N, R, S, Tです。サンプルコードを見ると、ゲインも変えようと思えば変えられるように見えます。

 

 

MAX31856はSPIでマイコンと通信します(第5回参照)。図のように接続してください。

 

 

そして、以下のライブラリをダウンロードします。

https://github.com/adafruit/Adafruit_MAX31856

以下のスケッチを使うと、シリアルモニタに温度計測結果が現れます。なお、ここではK型熱電対を使っています。

 



#include <Adafruit_MAX31856.h>

 

Adafruit_MAX31856 max = Adafruit_MAX31856(8, 11, 12, 13);

 

void setup()

{

  Serial.begin(9600);

  Serial.println("MAX31856 thermocouple test");

  max.begin();

  max.setThermocoupleType(MAX31856_TCTYPE_K);

}

 

void loop()

{

  Serial.print("Thermocouple Temp: "); Serial.println(max.readThermocoupleTemperature());

  delay(1000);

}

 

 

SDカードシールドとの併用

 

 ここまでは他のサイトにも書いてあると思いますが(何より、Adafruitのサイトそのものにわかりやすく書いてあります:英語が問題でなければ)、このMAX31856で計測した結果をSDカードに記録しようとしたところで、かなり苦戦しましたので、そのやり方について説明します。他のサイトにいろいろヘルプを求めましたが、見つかりませんでした。ついでに、多チャンネル化のために複数のMAX31856を使うことを考えます(ここでは例として2個)。問題は、他のSPIセンサーをつなぐとSDカードシールドとの通信ができなくなることでした。

 

 まず、SPI通信は、第5回で説明したように、6本の線を使います(VccとGNDで2本、厳密には残りの4本が通信用)。I2C通信と異なり、チップそのものがアドレスを持っていないため、個々のチップのCS(Chip Select)をArduinoの異なるデジタルピンにつなぎ、それを通して個々のチップを区別して通信をします(たとえば、1つ目のMAX31856のCSはピン8に、2つ目はピン9に、とつなぎ、「これからピン8のやつと通信しますよ」「次はピン9」として区別するわけです)。I2C通信と異なり、アドレス競合の問題がありませんが、デジタルピンの数しか接続できないという欠点もあります(実際にはバス拡張などで対応できるはずです)。まあとにかく、デジタルピンが3つ余っていれば、SDカードシールドとMAX31856を2個、計3つを接続できるはずです。多くのサイトには、CSを違うピンにつないで区別しましょう、とまでしか書いてありません。

 

 そこで、以下のように接続します。デジタルピンは8, 9, 10をCSに使います。MOSI・MISO・CLKは共通です。MAX31856モジュールの基盤にはSDO・SDIなどとありますが、それぞれMISO・MOSIと同じことです(Master-In, Slave-OutとMaster-Out, Slave-In:通信の方向を示します。SDOとSDIはSlave Data-OutとSlave Data-Inなのでしょう)。なお、昨今の人種差別問題の余波で、通信の世界でもmasterやslaveという呼び名はなくす運びにあるようです。ちなみに大きな寝室をマスターベッドルームと呼ぶのもタブーらしいです。マスターキーやマスタープラン、バーのマスターもダメになるのでしょうか。

 

 

ブレッドボード図だと煩雑でかえってわかりづらいかもしれません。ブレッドボードを省いて描くと、

 

 

ここで、電圧レギュレータなしのSDカードシールドを使っているので、SDカードは3.3V端子から印加しています。以下のスケッチを使います。ここで、最後のほうにあるSPI.end()という一文が重要です。これを入れないと、SDカードと通信とした次のループで、MAX31856と通信できなくなります。先ほどと同様に、CS(Chip Select)には8~10を使っています。

 



#include <SPI.h>

#include <SD.h>

#include <Adafruit_MAX31856.h>

 

const int TCSS1 = 8;

const int TCSS2 = 9;

const int chipSelect = 10;

 

Adafruit_MAX31856 max1 = Adafruit_MAX31856(TCSS1, 11, 12, 13);// Use software SPI: CS, DI, DO, CLK

Adafruit_MAX31856 max2 = Adafruit_MAX31856(TCSS2, 11, 12, 13);// Use software SPI: CS, DI, DO, CLK

 

void setup()

  Serial.begin(9600);

  delay(1000);

}

 

void loop()

{

  /* Start MAX31856 and read temperatures */

  max1.begin();

  max1.setThermocoupleType(MAX31856_TCTYPE_K);

  

  max2.begin();

  max2.setThermocoupleType(MAX31856_TCTYPE_K);

 

  float temp1=max1.readThermocoupleTemperature();

  float temp2=max2.readThermocoupleTemperature(); 

 

  /* Write the readings to SD card */

  SD.begin(chipSelect);

  File dataFile = SD.open("datalog.csv", FILE_WRITE);

  if (dataFile)

  {

    dataFile.print(temp1);dataFile.print(", ");dataFile.println(temp2);  

  }

  dataFile.close();

 

  SPI.end();

 

  delay(1000);

}

 

 

MAX31856を2つ使えたら、おそらく3つでも4つでも使えることでしょう。例えば三軸圧縮試験中、土供試体の上端・下端・側面など、いろいろな場所の温度が測れます(先端の防水処理だけは注意が必要。防水が甘いと、シースの隙間をつたって三軸セルの中から水が漏れてきます)。

 

実用例紹介

 

 ここで、北海道大学の地盤物性学研究室に導入した自作ロガーを紹介します。この装置は、一定荷重の下、粘土を凍結融解し、圧縮量の累積を計測していくもので、同じ原理の装置で行った研究は論文として公開されています(Nishimura et al., 2020)。ここでは、粘土供試体の上端・下端の温度を熱電対で計測し、荷重・変位をひずみゲージ型センサーで計測し(第19回参照)、LCDに表示するとともにSDカードに記録しています。このブログでいえば、第5、7、18、19、20回を読んでもらえれば作れます。それらを読まなくても、以下の通りつないで(ライブラリを入れたうえで)スケッチを入れればできますが。東京測器社製のハンドヘルドデータロガーを使っていたのですが、表示更新が遅く見づらいのと、ロードセルの分解能が低く、上載圧にして4kPa単位でしか表示できなったため、自作したという経緯があります。私はArduino Pro MiniやNanoといった小型のモデルを好んでいますが、ここでは5V(LCDを明るくするために必要)と3.3Vの出力端子を両方持つUNOをあえて使っています。

 

 

ちょっとした配置上の工夫点としては、HX711とMAX31856を上下に重ね、ビスで少し浮かした状態で固定していることです(下の写真)。二階建てにして、小さなユニバーサル基板の面積を有効利用しています。上のほうのユニバーサル基板上は不思議な配置に見えるかもしれません。他の装置で使っていたものを再利用しているからです。

 

 

 

いくつかの注意点です。

・第7回で紹介したRTCで時間を記録しています。

・HX711の読みにはセンサー固有の較正係数を掛けています。

・SDカードには10秒に1回だけ書き込むようにしています。

 

 



#include <DS3232RTC.h>

#include <Time.h>

#include <TimeLib.h>

#include <Wire.h>

#include <SPI.h>

#include <SD.h>

#include <Adafruit_MAX31856.h>

#include <LiquidCrystal_I2C.h>

#include <HX711.h>

 

const int TCSS1 = 8;

const int TCSS2 = 9;

const int chipSelect = 10;

const float CF1=0.00368;

const float CF2=0.00000091;

int rsec=0;

 

Adafruit_MAX31856 max1 = Adafruit_MAX31856(TCSS1, 11, 12, 13);// Use software SPI: CS, DI, DO, CLK

Adafruit_MAX31856 max2 = Adafruit_MAX31856(TCSS2, 11, 12, 13);// Use software SPI: CS, DI, DO, CLK

 

LiquidCrystal_I2C lcd(0x3F,16,2);

HX711 channel1;

HX711 channel2;

 

void setup()

  /* ----- Serial connection with PC ----- */

  Serial.begin(9600);

  while (!Serial) {

  ; // wait for serial port to connect. Needed for native USB port only

  }

 

  /* ----- Initialisation of LCD display ------ */

  lcd.init();

  lcd.backlight();

 

  /* ----- Initialisation of load cell amplifier ----- */

  channel1.begin(A0, A1);  // HX711.DOUT  - pin #A0, HX711.PD_SCK - pin #A1

  channel2.begin(A2, A3);  // HX711.DOUT  - pin #A2, HX711.PD_SCK - pin #A3

 

  /* ----- Initialisation of time ----- */

  //setTime(22, 29, 00, 28, 6, 2010);   // set the system time in Arduino [Hour, Min, Sec, Month, Day, Year]

  //RTC.set(now());   // set the RTC time according to Arduino (first time only, to reset the RTC)

  setSyncProvider(RTC.get);   // get the time from the RTC and set it to Arduino

  if(timeStatus() != timeSet)

    Serial.println("Unable to sync with the RTC");

  else

    Serial.println("RTC has set the system time");

 

  delay(1000);

}

 

void loop()

{

  max1.begin();

  max1.setThermocoupleType(MAX31856_TCTYPE_K);

 

  max2.begin();

  max2.setThermocoupleType(MAX31856_TCTYPE_K);

 

  float temp1=max1.readThermocoupleTemperature();

  float temp2=max2.readThermocoupleTemperature(); 

  float strain_s1=channel1.read_average(20)*CF1-43.8-95.6;

  float strain_s2=channel2.read_average(20)*CF2;

 

  digitalClockDisplay();

  Serial.print(temp1);Serial.print(", ");Serial.print(temp2);Serial.print(", ");Serial.print(strain_s1);Serial.print(", ");Serial.println(strain_s2);

 

  /* Write the readings to SD card */

  if(int(second()/10)!=rsec)

  {

    rsec=int(second()/10);

    SD.begin(chipSelect);

    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("  ,");       

      dataFile.print(temp1);dataFile.print(", ");dataFile.print(temp2);dataFile.print(", ");dataFile.print(strain_s1);dataFile.print(", ");dataFile.println(strain_s2);  

    }

    dataFile.close();

  }

 

  SPI.end();

 

  /* Display to LCD */

  lcd.clear();

  lcd.setCursor(0, 0);lcd.print(temp1);

  lcd.setCursor(7, 0);lcd.print(" ");

  lcd.setCursor(8, 0);lcd.print(strain_s1);

  lcd.setCursor(0, 1);lcd.print(temp2);

  lcd.setCursor(7, 1);lcd.print(" ");        

  lcd.setCursor(8, 1);lcd.print(strain_s2);

 

  //delay(1000);

}

 

/* Function to display time on PC via serial */

void digitalClockDisplay(void)

{

    Serial.print(hour());

    printDigits(minute());

    printDigits(second());

    Serial.print(' ');

    Serial.print(day());

    Serial.print(' ');

    Serial.print(month());

    Serial.print(' ');

    Serial.print(year());

    Serial.println();

}

 

/* Function to adjust the digits of minute and second */

void printDigits(int digits)

{

    Serial.print(':');

    if(digits < 10)

        Serial.print('0');

    Serial.print(digits);

}

 

 

参考文献

Nishimura, S., Okajima, S., Joshi, B. R., Higo, Y. and Tokoro, T. (2020) Volumetric behaviour of clays under freeze-thaw cycles. Géotechnique, Under production. DOI: 10.1680/jgeot.20.P.047

 しばらく休みをとった後、本ブログSeason 2の開始です(後からですみませんが、第1回のブログで全体の予定目次を更新しておきました)。しばらく、いろいろなセンサーの使用法について紹介していこうと思います。今回は、力学センサーを多く扱う土木系でおなじみのひずみゲージ型センサーからデータを取得する方法を紹介します。

 

 ひずみゲージ型センサーは、ご存じの通り、ブリッジに数Vの電圧を印加すると、圧力・力・トルク・変位などが(ひずみゲージの曲げを通して)数mVの電圧として返ってくるもので、計測する物理量の変化と出力電圧が比例しています。数mVという出力は非常に小さいので、普通はこれをアンプを通して増幅し、ADを介してデジタル化してPCで記録するか、あるいはアンプ・AD・記録装置が一体化したデータロガーで記録します。ここでは、後者を自作しようというものです。

 

ロードセルコンバータ(ひずみゲージ型センサー用アンプ・AD変換モジュール)HX711

 

 といっても、大した工作は必要なく、HX711というチップを利用するだけです。例によって、ブレークアウトモジュールとして基板実装されたものを使うと便利です。AmazonなどでHX711を検索すると、1枚300円くらいのものが出品されています。

 

 

HX711のブレークアウトモジュールには、出力側が「RED/BLK/WHT/GRN/YLW」のもの(赤い基板のものが多い:上写真右)と「E+/E-/A-/A+/B-/B+」のもの(緑の基板のものが多い:上写真左)がありますが、どちらも基本的に同じで(載っているチップは同じですので)、ピンアウトが微妙に違います。このチップは2つのひずみゲージ型センサーをつなぐことができ、それぞれチャネルAとチャネルBとしています。チャネルAのほうはゲイン(利得)が64倍あるいは128倍から選べる一方で、チャネルBはゲインが32倍に限られています。ゲインは大きいほうがよいという場合が多いので(128倍でもレンジオーバーはしない)、例えば4つのセンサーを扱う必要がある場合は、モジュールを2つ買ってA・Bチャネル両方使うよりも、チャネルBは使わずに4つ買ったほうがよいと思います(どうせ安いものなので)。このチップはアンプに加えて24bitのADコンバータが内蔵されており、センサーの出力をデジタルデータとしてマイコン(Arduino)に送ります。

 

接続方法

 

 まずセンサーの接続について説明します。上記写真の右のモジュールの場合、端子に色が書かれているので、その通りにセンサーからの印加+、印加-、出力+、出力-、グラウンドをそれぞれRED、BLK、WHT、GRN、YLWにつなぎます。共和電業や東京測器の製品なら、色のまま(シールド線はYLWに)つなげば大丈夫です。基板には特段に書いてありませんが、これが上記の「チャネルA」に接続したことになります。チャネルBは、写真のCLK・GND端子の下にある二つの穴が出力+、出力-の端子になっているようです(使ったことがありませんが)。なお、緑の基板のモジュールのタイプでは、センサー接続端子がE+, E-, A-, A+, B-, B+となっているため、チャネルAを使いたければ最初の4つに印加+、印加-、出力-、出力+をつなぎます。シールドを接続する端子がありませんが、Arduino接続側のGND端子につないでおけばよいでしょう(赤い基板のモジュールでも、YLWは単にGNDに直結されているようです)。

 

 次に、マイコン(Arduino)側ですが、VCC、DAT、CLK、GNDの4本を使います。VCCは5Vあるいは3.3V、GNDはGNDにつなぎます。DAT、CLKはそれぞれデータ線とクロック線だと思いますが、これらは任意のアナログピン(Arduino UNOならA0-A5)あるいはデジタルピン(0-13)につなぐことができ、ソフトウェアのほうで指定します。これはI2CともSPIとも異なる通信プロトコルのようです。

 

 赤い基板のモジュールのほうを例にした接続図が以下になります。

 

 

 

 なお、センサーをいちいちモジュールにはんだ付けするのは大変なので、端子台とユニバーサル基板などを組み合わせたものを作っておくとよいです。

 

 

スケッチは以下のようにします。HX711用のライブラリをダウンロードしてincludeしてください(第7回参照)。

https://github.com/bogde/HX711

 



#include <HX711.h>

 

HX711 channel1;

 

void setup() {

  Serial.begin(9600);

  channel1.begin(A0, A1);  // HX711.DOUT  - pin #A1, HX711.PD_SCK - pin #A0

}

 

void loop() {

  Serial.println(channel1.read_average(20));       // print the average of 20 readings from the ADC

  delay(1000);

}

 

 

注意点として、

・ゲインはデフォルトで128。64に変えたければ、channel1.set_gain(64);の一文を加える(このスケッチでいえば、setup(){}の最後に)。

・データレートはデフォルトで10Hz。データシートによれば80Hzにも変えられるよう。

・このスケッチでは、データ取得速度よりも正確性を求めて20回平均をとっています。10Hzで20回なので、それだけで2秒待つことになります。平均をとらずに単に1回の計測値を取得したければ、channel1.read()という関数があります。

 

 なお、上記ライブラリにはいろいろな関数があります。スケールを較正する関数やゼロ点をとるtareの関数などがありますが、基本的にこれらは不要と思います。どのような数値が得られるにしろ、Arduinoに演算をさせればよいですから。センサーメーカーからの較正係数やゲインに基づいて較正係数を計算する人も多いと思いますが、このモジュールから送られてくる数値は非常に大きく、何の数値なんだかよくわかりません。24bitなのに2^24よりも大きな数値が送られてきたり。しかし、物理量と比例はしているので、なんだかよくわからずも較正してそのまま使っています。

 

計測例

 

 土質実験室ではロードセル(荷重計)や変位計を使うことが多いですが、これらを用いた計測結果の例を示します。ロードセルは共和電業のLUK-A(引張・圧縮両用、容量5kN)です。較正して物理量に直した結果を示します。線形性には問題なく、ここでは精度・安定性のみ示します。ロードセルに何も置かず、放置した結果なのですが、0.005kgf程度の精度が見られます。容量-5~+5kNに対して0.005kgfなので十分といえるでしょう。荷重を変えた後、少しドリフトが見られたのですが(ここで見せているのは、数十秒待って落ち着いた後のデータです)、モジュールのせいなのかロードセルのせいなのか、そこまでは調べませんでした。

 

 

 もう一つの例は変位計です。実験室にうっちゃってあったもので、ラベルがないのでどこの会社の製品かわかりませんが、ストロークが10mmの、ごく一般的な変位計です。東京測器CDP-Mに形が似たものです。これに少し変位を与えて固定しておいた結果が以下になります。

 

 

多少のドリフトがありますが(これはジグのクリープかもしれません。この程度の変位だと、温度変化の影響も受けます)、素晴らしい安定性・精度です。どこかのブログで、HX711はノイズに弱く安定性に問題あり、と書いてありましたが、これらの例では素晴らしい結果が得られました。そのブログの人は、違う分野の人で、もっと高い精度を求めているのか?あるいはシールドケーブルを使っていなかったのか?

 

複数のセンサーから読む

 

 先に記したように、ゲイン設定の制約などを考慮すると、センサーの数だけHX711を用意したほうが得策です。例えば2つ接続する場合、2つ目のHX711のDAT・CLKは例えばA2・A3など未使用のピンに接続します。そして、スケッチでは個別のHX711のインスタンスを作成すればよいです。

 



#include <HX711.h>   

 

HX711 channel1;

HX711 channel2;

 

void setup() {

  Serial.begin(9600);

  channel1.begin(A0, A1);  // HX711.DOUT  - pin #A1, HX711.PD_SCK - pin #A0

  channel2.begin(A2, A3);  // HX711.DOUT  - pin #A1, HX711.PD_SCK - pin #A0

}

 

void loop() {

  Serial.println(channel1.read_average(20));       // print the average of 20 readings from the ADC

  Serial.println(channel2.read_average(20));       // print the average of 20 readings from the ADC

 

  delay(1000);

}

 

 

 ちなみに、後の回で紹介する予定ですが、Arduinoからのシリアル通信はArduino IDEの「シリアルモニタ」を使わなくても、たとえばVisual BasicのSerialPort(VB6以前でいうMS Comm)などのターミナルモジュールで受け付けることができるので、HX711とArduinoがあれば、Windows PC制御の三軸試験装置がつくれるということになります。北海道大学の地盤物性学研究室では、力学試験装置にはUnipulseのロードセルコンバータLC240などをセンサー1チャネルごとに使っていますが、これは1個8万円くらいします。三軸試験装置では4~5チャネル(軸荷重、セル圧、間隙水圧、排水量、軸変位)の計測が必要になるので、これだけで40万円くらいになってしまいます。もちろん、高いぶんの価値はある仕様なのでしょうが、ちょっとした装置なら、HX711×5個+Arduino互換品で、3000円くらいでやってみるということもできます。実際に、同研究室の一次元凍結融解繰返し装置や連続加圧式水分特性曲線測定装置は、このような自作モジュールで動いています。後の回で紹介したいと思います。

 

 

 さて突然ですが、Season 1は今回で終わりです。ここまでで、フィールドでセンサーの電圧を測るという目標に、実装まで含めてたどり着いたからです。最後に、LCD(液晶ディスプレイ)に情報を表示させる方法を紹介します。フィールド用ロガーには、基本的に情報を表示するインターフェイスは不要(誰も見ていないので)ですし、その場で計測値を知りたければ、PCにつないでシリアル通信で表示するという方法をとってきました。しかし、PCをいちいちつなぐのは面倒ですし、小さなLCDに出力できると便利なこともあります。特に、センサーを地盤に埋設する前後でのオフセットの変化などを記録しておくときに便利です。

 

 LCDにもいくつか製品があるわけですが、16文字×2行のものと20文字×4行のものが多く売っています。後者のほうが少しだけ高いですが、表示できる情報量が大きく違うので、多チャンネルのロガーに対しては後者のほうが便利なことが多いです。「LCD Arduino」など打ち込んでAmazonなどで検索してください。

 

左が16文字×2行、右が20文字×4行

 

 基盤の端にたくさんの端子がありますが、ここにArduinoを直接つなぐよりも、コントローラーをつけてI2C通信で制御するのが便利です。LCDを買ったら、このコントローラーが最初からはんだ付けされている場合もありますが、別途購入して、自分でつけないといけないこともあります。

 

持っているのがコントローラー。安いもんです。

 

その後、下図のように接続してみて下さい。ここでは、アナログピンA0に入力する電圧を1秒おきにLCDに時刻とともに表示します。RTCモジュールを接続していないので、Arduinoのデフォルトの時間から始まってしまいますが(だいたい1970/1/1 0:00)、RTCを使って現在の時間を正しく表示する方法は第7回で説明しています。

 

 

 

 以下のスケッチを使ってください。ここで、LCDのアドレスですが、製品によって異なります。0x27か0x3Fのどちらかのことが多いので試してください。これらのどちらでも動かない場合、下記「参考1」のスケッチで調べることができます。また、ここでLiquidCrystal_I2Cというライブラリが必要になりますが(ライブラリの保存については第7回参照)、以下から手に入ります。

OsoyooのHP

https://osoyoo.com/ja/2016/11/09/16x2-i2c-liquidcrystal-displaylcd-for-uno-r3-and-mega2560/

GitHub

https://github.com/fdebrabander/Arduino-LiquidCrystal-I2C-library

これらの場所から落としてくるLiquidCrystal_I2Cというライブラリは、名前は同じようですが、中身は少し違うようです。ここではOsoyooのものを使っていますが、おそらくどちらも同じような使い方をするはずです。

 



#include <Wire.h>
#include <TimeLib.h>
#include <LiquidCrystal_I2C.h>
 
LiquidCrystal_I2C lcd(0x27,20,4);
 
void setup(void)
{
  /* ----- Setting up serial communication with PC ------ */     
  Serial.begin(9600);
  while (!Serial) {
  ; // wait for serial port to connect. Needed for native USB port only
  }
 
  /* ----- Initialisation of LCD display ------ */
  lcd.init();
  lcd.backlight();
}
 
void loop(void)
{
  /* Read voltage to A0 pin and convert it to [mV] */
  float millivolt;
  millivolt=analogRead(A0)*5000.0/1023;
 
  /* Clear display of text from previous loop */
  lcd.clear();
 
  /* LCD 1st row: Time */
  lcd.setCursor(0, 0);lcd.print(year());
  lcd.setCursor(4, 0);lcd.print("/");
  lcd.setCursor(5, 0);lcd.print(month());
  lcd.setCursor(7, 0);lcd.print("/");
  lcd.setCursor(8, 0);lcd.print(day());
  lcd.setCursor(10, 0);lcd.print(" ");
  lcd.setCursor(11, 0);lcd.print(hour());
  lcd.setCursor(13, 0);lcd.print(":");
  lcd.setCursor(14, 0);lcd.print(minute());
  lcd.setCursor(16, 0);lcd.print(":");
  lcd.setCursor(17, 0);lcd.print(second());
 
  /* LCD 2nd row: Voltage */
  lcd.setCursor(0, 1);lcd.print(millivolt);
  lcd.setCursor(6, 1);lcd.print("mV");
  delay(1000);
}
 

 

ここで、lcd.setCursorという関数でlcd.setCursor(0, 0)などとしてカーソル位置(文字開始位置)を決めますが、0列0行から数えることに注意してください。20文字4行のLCDなら、(19, 3)が最後の文字ということになります。

 

このように表示されましたか?

 

参考1

Osoyooさんが公開している、I2C機器のアドレスを探すスケッチです。アドレスを知りたいI2C機器をつないで走らせてみて下さい。PCのシリアルモニタにアドレスが表示されます。

 



/* this program is to detect IC2 device connected to Arduino

 * project tutorial see http://osoyoo.com/?p=72

 */

#include <Wire.h>

 

void setup()

{

  Wire.begin();

 

  Serial.begin(9600);

  Serial.println("\nI2C Scanner");

}

 

void loop()

{

  byte error, address;

  int nDevices;

 

  Serial.println("Scanning...");

 

  nDevices = 0;

  for(address = 1; address < 127; address++ )

  {

    // The i2c_scanner uses the return value of

    // the Write.endTransmisstion to see if

    // a device did acknowledge to the address.

    Wire.beginTransmission(address);

    error = Wire.endTransmission();

 

    if (error == 0)

    {

      Serial.print("I2C device found at address 0x");

      if (address<16)

        Serial.print("0");

      Serial.print(address,HEX);

      Serial.println("  !");

 

      nDevices++;

    }

    else if (error==4)

    {

      Serial.print("Unknow error at address 0x");

      if (address<16)

        Serial.print("0");

      Serial.println(address,HEX);

    }   

  }

  if (nDevices == 0)

    Serial.println("No I2C devices found\n");

  else

    Serial.println("done\n");

 

  delay(5000);           // wait 5 seconds for next scan

}

 

 

参考2

このOsoyooさんのページにはLCDの使い方が詳しく書いてあります。おそらく日本語のうまい中国人?が書いたと思われるので微妙な日本語ですが、わかりやすいです。

https://osoyoo.com/ja/2016/11/09/16x2-i2c-liquidcrystal-displaylcd-for-uno-r3-and-mega2560/

 

参考3

LCDのコントローラーにはポテンショメーターがついていて、+ドライバーで回すとバックライトの輝度を変えられます。あれ、つかない?と思ったら、単にディスプレイのコントラストが低くて見えていなかっただけ、ということがあるので、回してみてください。また、私の持っているLCDは、3.3V系では薄すぎて見えません。5Vで動かす必要があります。Arduino Pro Mini 3.3Vモデルにも、RAW(3.3Vに下げてから入力する端子)ではなくVcc(与えた電圧をそのままかける端子)に無理やり5V流して(不思議なことに、壊れたことがありません)使わないと、表示が見えません。

 ここまでで、電圧を継続的に記録する装置の中身はできました。これをケースに入れて観測サイトに設置するわけですが、実はこれが奥が深い。どのようなケースにどのように入れ、どこに置くのか、センスと経験が必要なところです。管理人の経験からいうと、原則は

 

(i) 可能なら大きなケースを使う。小さなケースにする必要があるなら、なるべく実験室で全て組み立ててから行く。サイトで細かい仕事をしようと思うな。

(ii) 小さなケースでぴったり作ると、例えば後ほどチャネル数を増やすといった拡張作業は非常に困難

(iii) 端子台は可能なかぎり接続しやすいものに。ネジ式よりもPCBターミナルブロックなどのほうがよい。

(iv) 金属製よりもプラスチック製のほうが扱いやすい(ケーブルを通す穴を空けるときなど)

 

とにかく、サイトで細かい作業はなかなかできるものではありません。地盤工学への適用として、法面で作業することも多いと思いますが、勾配が1:2を超えると、立っているだけでも足首に負担がかかります。そして、無風・快晴の日ばかりではありません。天気が良すぎると、まぶしくて細かいものが見えないこともあります。細かいネジなど回していると、あっという間に日が暮れます。多少の雨ならタープを組み立てて作業ができますが、雪だと横から入ってきます。

 

上記(i)に関して、下の写真はプロに頼んで構成してもらったロガーとボックスです。かなり大きくて余裕があることがわかります。

 

 

一方、これは管理人が自作したものです。コンパクトにまとめたという意味で、搬送その他は楽でいいのですが、原位置での作業は(この状態になるように接続するのは)大変でした。連れていった学生がずっと文句を言っていました。

 

 

 Amazon・Monotaro・オレンジブックなどで「プラスチック ボックス」などと検索すると無数に製品がヒットするので、好きなものを選べばよいのですが、いくつか紹介します。

 

・防水プールボックス

 未来工業などのものが多く出回っています。かなりコンパクトなシステムとして収めることができます(ただ、上記の通り、けっこう大変)。たとえばこれは未来工業製の100mm×100mm×75mmのもの。写真のようにぎっちり詰めることになりますが、カバンに入れて持ち運べます。中に取付板などはないので、自分で何かしら作ることになります(内壁にユニバーサル基板を直接ねじ止めする、あるいは100円ショップで買った木板をちょうどよい大きさに切って両面テープで貼って取付台にする、など)。

 

 

 

・TAKACHIのプラボックス

 こちらはいろいろなサイズで売っています。

タカチ電機工業 BCAP型防水・防塵開閉式

https://www.monotaro.com/g/00965864/?t.q=%82a%82b%82%60%82o%8C%5E%96h%90%85%81E%96h%90o%8AJ%95%C2%8E%AE%83v%83%89%83%7B%83b%83N%83X

 

ちょうどよいスケールがなかったので傘と一緒に。

 

 

こんな小さいものもあります。

 

 

プラスチックなのでケーブルを通す穴が開けやすくよいです(底面の部分)。

 

 

取付台は別売りです。

https://www.monotaro.com/g/00965875/?t.q=%82a%82b%82%60%82o%8C%5E%96h%90%85%81E%96h%90o%8AJ%95%C2%8E%AE%83v%83%89%83%7B%83b%83N%83X

 

 

100円ショップの木板がぴったり合う場合もあります。100円ショップの木板はカッターで切れるほどフニャフニャで強度は全くありませんが、その分、切削は楽で、ちょうどこのような用途に向いています。

 

 この他、「対候性」と書いたボックスも多く売られていますが、これらは必ずしも「防水」を意味しないので注意してください。当然ですが、ゴムパッキンのないケースに防水効果はありません。ぴったりと閉まっているように見えても、雨水は侵入します。

 

 ケーブル用の穴は、当然、雨水が入らないように下の面に開けますが、ケーブルを通したらエアコン用パテなどで隙間をなるべく塞いでおくとよいです。そうでないと虫が入ります。北海道ではマイマイガの幼虫の巣ができたり、場所によってはスズメバチが入っていたりします。蜘蛛はしょっちゅうです。

 

 

 こういったケースは数千円はするので、自作ロガーは、中身の部品すべて足し合わせてもまだケースのほうが高い、というのは冗談ではないというのがわかります。