ここから「実践編」になります。基本的に、「基礎編」(第1~18回)と「発展編」(第19~34回)を読んで理解できれば、ここで紹介する装置はこれ以上の説明なしに作れるのですが、料理本のように必要部品と接続図・スケッチを載せておけば便利かと思って、いろいろ紹介していきたいと思います。個々の部品のはたらきの説明は省略します(過去の回への参照を載せます)。要は、「書かれている通りにつないで、書かれているスケッチを入れればよい」というレシピ本にしたいと思います。

 

 ここでは、段階圧密試験用データロガーのレシピを載せます。「段階圧密試験用」といっても、要はひずみゲージ型変位計用の1chロガーです。他の用途にも当然使えますし、4ゲージのひずみゲージ型センサーならロードセルでも何でも使えます。今までのブログを読んでくれた人は、特段にここを読まなくても作ることができると思いますが、復習も兼ねてこの回を設けます。ディップスイッチを使って、記録のインターバルを調整する機能も持たせます。「基礎編」で時間を記録するためのRTC(Real-Time Clock)と、データをSDカードに残すためのSDカードシールドをそれぞれ第7回、第5回で説明しました。これらを個別に組み込んでもよいのですが、これらを一体にした「データロガーシールド」なるものも売っています(シールドというのは、マイコンボードにぴったりとはまるように作られたボードのことを指すことが多いです。ぴったりはまらなくてもシールドと呼ぶことはありますが)。これを買うと、接続の手間が省けます。データロガーシールドについているRTCはDS3231ではなくDS1307が多いようです。Arduinoの標準ライブラリで制御でき、むしろ簡単です。

 

  

データロガーシールド:左がArduino Nano用(表と裏)、右がArduino Uno用(表)。コイン電池には小さめのCR1220がはまる(100円ショップで2個100円)。Nano用にはMicro SDカードが、Uno用にはSDカードがはまる

 

 このロガーを買って、SDカードに記録されない、という問題がある場合は、容量の小さいSDカードを試してみて下さい。容量4GB程度までが無難です(SDHCには対応していない場合があります)。

 

 段階圧密試験用データロガー(データロガーシールド利用版:Arduino Nano) 

 

■  基本性能

 4ゲージのひずみゲージ型センサー(段階圧密試験では変位計)の読みを24-bit分解能のHX711で取得し、毎秒LCDに表示する。Micro SDカードへの記録はDIPスイッチにより8段階(記録なし、1sec, 10sec, 30sec, 1min, 10min, 30min, 60min:ただし、モードが切り替わった際には、前回記録からの間隔ではなく、00秒あるいは00分からの間隔)に手動切り替えできる。

 

■  必要部品*

*価格は執筆の時点でAmazonで見つけた最安値に基づいて単価として表しています。「\320」とは「5個セット\1,600」という意味の場合もあります。

 

コア部

・Arduino Uno・・・互換品 \670

・Arduino Uno用データロガーシールド・・・\665

・HX711ブレークアウトモジュール(ひずみゲージアンプ)・・・\143

・LCDモジュール(16×2文字:I2Cコント―ラ付き)・・・\320

・DIPスイッチ8ピン4Pポジション・・・\63

・基板表面実装用10kΩ抵抗×4・・・\8(300個入り\612)

・ユニバーサル基板4cm×6cm・・・\100

・コイン電池(CR1220)・・・\55

・Micro SD 4GB・・・\438

・その他、ジャンパーワイヤーの切れ端少々

計 約\2,500

 

付属部

・コア部を固定する木版とネジ

・(変位計のシールドケーブル先がバラなら)端子台5極

・(変位計のシールドケーブル先がNDIS端子なら)7極NDIS端子メス

・(スケッチ書き込みおよび給電のため)USB Type A - Type Bケーブル

(電池から給電する場合は電池ボックス+電池でもよい)

・(家庭用AC100Vコンセントから給電する場合)USB Type A用ACアダプタ

・ロガーをON/OFFするためのスライドスイッチ

 

 

■  必要外部ライブラリ

LiquidCrystal_I2C.h(第18回参照)

HX711.h(第19回参照)

 

■  接続図

HX711ブレークアウトモジュールの形状に応じて2種示します(第19回参照)。ディップスイッチは3極(D6~D4)しか使わないのですが、3極のDIPスイッチというのはあまり売っていないようで、将来の拡張を見越して4極接続しています。スケッチではD6~D4しか使っていません(D6をLSB、D4をHSBとして扱っています。LSB: Least Significant Bitとは2進数の一番下の桁(ここでは1の桁)で、MSB: Most Significant Bitとは一番上の桁(ここでは4の桁)のことです)。

 

 

 

 

■  出来姿

Arduino Uno、HX711、LCDの3つのモジュールは、ユニバーサル基板へのはんだの直付けを避け、簡単に再利用できるように組みました。それぞれが損傷した際の交換も簡単です。下の写真は表面・裏面の様子です。裏面には抵抗が4つ並んでいますが、この下にディップスイッチがあり赤い線(5V)につながっています。表面の左上に赤いスライドスイッチがありますが、これは同じロガーを並べた際に。電源配線をタコ足にして個々のロガーをON/OFFできるようにするためのもので、この時点では使っていません(裏面左下を見てわかる通り)。

 

 

なお、このブログではI2CのSDA・SCLをそれぞれ緑・青にしてきましたが、ここでは逆転させています。というのも、新しく買ったジャンパーワイヤーが、先端ソケットが互いに切り離せないタイプで、色の並びの順番を変えられなかったので・・・

 

■  スケッチ

 留意点は以下の通りです。

・スケッチ中のコメントにあるように、RTC.adjust(DateTime(__DATE__, __TIME__));の一文は、RTCの時間合わせです。これにより、RTCがスケッチのコンパイル時のPCの時間に合わせられるようです。これをArduinoに書き込んだままにしておくと、電源がリセットされるたびにコンパイル時の時間にRTCが戻されてしまいます。上の一文を有効化したうえでまず書き込み、Arduinoを走らせてRTCの時間合わせをしたら、この一文を無効化(文頭に//を加えてコメント文にする)したうえでもう一度書き込みます。これで、ロガーシールドにはめたコイン電池CR1220を外すまで時間は刻み続けられます。

・LcdTimeDisplay()という関数を定義しています。LCDに表示する際に、これがないと、例えば12:43:59の1秒後は、12:43:09になってしまいます(now.second()は00秒ではなく0秒なので、0が一つ、「:」の次に書かれてしまう)。これを12:43:00にするための関数です。

・channel1.read_average(10)とあるように、変位計の値は10回読んで平均をとっています。第19回で説明したように、HX711のサンプリング間隔はデフォルトで0.1secなので、10回読むと1秒になります。ここ以外にほとんど時間を使うタスクがないスケッチなので、これでループさせると動作はほぼ1秒間隔になります(厳密には1秒とちょっとかかっているわけですが)。

・冒頭にCF0とCF1の値を決める箇所があります。これがセンサーの較正係数(それぞれオフセットと電圧に対する感度)になります。まずCF0=0、CF1=1として較正をしてから、それぞれの係数を書き込み、またそのスケッチをArduinoに書き込む必要があります。

 

 

#include <SPI.h>

#include <SD.h>

#include <Wire.h>

#include "RTClib.h"

#include <Time.h>

#include <TimeLib.h>

#include <LiquidCrystal_I2C.h>

#include "HX711.h"

 

RTC_DS1307 RTC;

LiquidCrystal_I2C lcd(0x27,16,2);

HX711 channel1;

const int chipSelect = 10;

File dataFile;

 

int rec_mode=0;

bool rec_flag=false;

int prev_sec=0;

int prev_min=0;

int prev_hour=0;

 

// Calibration factors for the sensor

float CF0=0; // [mm]: Offset at 0 reading

float CF1=1; // [mm/reading]: Slope

 

void setup(void)

{

  // Preparation for dip switches

  pinMode(3, INPUT);

  pinMode(4, INPUT);

  pinMode(5, INPUT);

  pinMode(6, INPUT);

 

  Serial.begin(9600);

 

  // 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");

  }

  Serial.println("card initialized.");

 

  // RTC

  Wire.begin();

  RTC.begin();

  delay(1000);

 

  if(!RTC.begin())

  {

    Serial.println("RTC failed");

  }

  else

  {

    // 次の一文を有効化して書き込み、まず走らせる。それでRTCの時間がPCの時間に合わせられる。

    // 次にこの文をコメントアウトしてして書き込む(そうしないと、電源ONのたびにスケッチコンパイルの

    // 時間にリセットされてしまう)   

    RTC.adjust(DateTime(__DATE__, __TIME__));

  }

 

  // HX711

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

   

  // LCD

  lcd.init();

  lcd.backlight();

}

 

void loop(void)

{

  DateTime now;

 

  // fetch the time

  now = RTC.now();

 

  // Displacement sensor reading: Convert to [mm]

  float sensor1=CF0+channel1.read_average(10)*CF1;

 

  // Update the recording mode

  rec_mode=4*digitalRead(4)+2*digitalRead(5)+digitalRead(6);

 

  // Judge the SD card recording flag

  switch(rec_mode)

  {

    case 0: // No recording

      rec_flag=false;

      break;

    case 1: // Every second

      rec_flag=true;

      break;

    case 2: // Every 10 seconds

      if(now.second()%10==0) rec_flag=true;

      else rec_flag=false;

      break;

    case 3: // Every 30 seconds

      if((now.second()%30==0)) rec_flag=true;

      else rec_flag=false;

      break;

    case 4: // Every minute

      if(now.second()==0) rec_flag=true;

      else rec_flag=false;  

      break;

    case 5: // Every 10 minutes

      if((now.minute()%10==0)&&(now.minute()!=prev_min))

      {

        rec_flag=true;

        prev_min=now.minute();

      }     

      else rec_flag=false;  

      break;

    case 6: // Every 30 minutes

      if((now.minute()%30==0)&&(now.minute()!=prev_min))

      {

        rec_flag=true;

        prev_min=now.minute();

      }     

      else rec_flag=false;   

      break;

    case 7: // Every hour

      if((now.hour()!=prev_hour))

      {

        rec_flag=true;

        prev_hour=now.hour();

      }     

      else rec_flag=false;  

      break;

  }

 

  // Display to Serial Monitor

  Serial.print(now.year(), DEC);

  Serial.print("/");

  Serial.print(now.month(), DEC);

  Serial.print("/");

  Serial.print(now.day(), DEC);

  Serial.print(" ");

  Serial.print(now.hour(), DEC);

  Serial.print(":");

  Serial.print(now.minute(), DEC);

  Serial.print(":");

  Serial.print(now.second(), DEC);

  Serial.print(", ");

  Serial.print(sensor1);

  if(rec_flag) Serial.println(" -> Recorded to SD");

  else Serial.println("");

  dataFile.println("");

 

  // Display to LCD

  LcdTimeDisplay(0, 0, now.month()); 

  lcd.setCursor(2, 0);lcd.print("/"); 

  LcdTimeDisplay(3, 0, now.day()); 

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

  LcdTimeDisplay(6, 0, now.hour()); 

  lcd.setCursor(8, 0);lcd.print(":");

  LcdTimeDisplay(9, 0, now.minute()); 

  lcd.setCursor(11, 0);lcd.print(":");

  LcdTimeDisplay(12, 0, now.second()); 

 

  lcd.setCursor(0, 1);

  switch(rec_mode)

  {

    case 0: lcd.print("NoRec"); break;

    case 1: lcd.print(" 1sec"); break;

    case 2: lcd.print("10sec"); break;

    case 3: lcd.print("30sec"); break;

    case 4: lcd.print(" 1min"); break;

    case 5: lcd.print("10min"); break;

    case 6: lcd.print("30min"); break;

    case 7: lcd.print("1hour"); break;

  }

   

  lcd.setCursor(6, 1);lcd.print(sensor1);

  lcd.setCursor(12, 1);lcd.print("mm  ");

   

  // Write to SD card

  if(rec_flag)

  {

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

    dataFile.print(now.year(), DEC);

    dataFile.print("/");

    dataFile.print(now.month(), DEC);

    dataFile.print("/");

    dataFile.print(now.day(), DEC);

    dataFile.print(" ");

    dataFile.print(now.hour(), DEC);

    dataFile.print(":");

    dataFile.print(now.minute(), DEC);

    dataFile.print(":");

    dataFile.print(now.second(), DEC);

    dataFile.print(", ");

    dataFile.print(sensor1); 

    dataFile.println("");

    dataFile.close();

    rec_flag=0;

  }

}

 

void LcdTimeDisplay(int x, int y, int a)

{

  if(a<10)

  {

    lcd.setCursor(x, y);

    lcd.print("0");       

    lcd.setCursor(x+1, y);

    lcd.print(a);   

  }

  else

  {

    lcd.setCursor(x, y);

    lcd.print(a);      

  }

}

 

 

このスケッチを使った時に、ディップスイッチを使ってSDカード記録間隔を設定する際の早見図です。

 

 

 下の写真は北海道大学にある4連の段階圧密試験装置です。しばらくの間死んでいましたが、実験のクラス用にリノベーションしたいと思っています。4連なので、同じロガーを4つつくりました。もちろん、1つのマイコンで4つの変位計の計測値を記録することも可能ですが、学生実験のときは4つのグループが個別に作業するので、完全独立のほうがいろいろとよかろうと思い、同じものを4つつくりました。NDISコネクタで変位計がワンタッチでつなげられるようにしています。NDISコネクタもブラケットもモノタロウで買えます。

 

 

 

基盤を取付ベース(木板)に木ネジで固定するときのコツです。基板の裏にはいろいろついているので、そのまま木板に押し付けて固定するわけにはいかず、浮かせる形にしますが、そのためのスペーサーとして、土質試験室によくある空気圧や排水用のナイロンチューブを使うとよいです。下の写真では、ニッタ・ムアーの外径6mm・内径4mmのナイロンチューブ(青)を使っています。これを5mm~10mmくらいに切るとスペーサーになります。

 

 

先の写真では、個々のロガーを4つ付けただけなので、電源が個々に4個必要です。これをタコ足配線し、1つの電源だけを共有するとともに、個々のロガーのON/OFFができるように、途中に赤いスライドスイッチを付けたのが下の写真です。電圧は、ロガーシールドについているPBCターミナルブロック(緑の端子台)から与えています。ロガーシールド内の詳しい配線はわかりませんが、ここに与えた電圧は電圧レギュレータを通ってArduino Nanoに印加されるようなので、5Vよりやや大きい電圧が必要です(写真ではエネループ6本で7.7V与えています。5Vちょうどを与えると3.8VくらいになってArduino内部に与えられるので、正常に動きません)。Arduino NanoのUSB端子に直接電圧を与える場合は逆に、5Vちょうどでないといけないはずです。

 

 

これでそれぞれの赤いスライドスイッチをONすると・・・

 

 

動き始めます。変位計をつないでいない状態の写真なので、変位の値は不定値です(ちょっと時間合わせが甘いか)。LCDの上段に時刻、左下にSDカードへの記録間隔設定を示しています。電池で印加しているといずれ切れるので、9Vのアダプタを使って印加しようと思います。