ESP32-WROOM-32Eで加速度センサーの情報を取得する

組み込みエンジニア

こんにちは、ENGかぴです。

ESP32-WROOM-32EのWire(SPI)を使用すると加速度センサー(ADXL345)の加速度やタップ情報などを取得することができます。ADXL345のライブラリを使用して加速度センサーの情報を取得する方法をまとめました。

ADXL345が検出したイベントを割り込みを使用して取得しています。ESP32シリーズでDIの変化による割り込みの使い方をまとめています。ADXL345モジュール(秋月電子)を使用して加速度を取得しています。

ESP32-WROOM-32Eで動作確認したことについてリンクをまとめています。

ESP32-WROOM-32Eで学べるソフト開発と標準ライブラリの使い方

加速度センサーのライブラリを使用する

ADXL345の製造元のアナログデバイセズのHPに提供されているライブラリを使用してもよいのですが、今回はSeeeduino用に有志が提供している「Accelerometer ADXL345」を使用します。

ADXL345のライブラリをインストールする

Arduino IDEのライブラリマネージャ
Arduino IDEのライブラリマネージャ

Arduino IDEを使ってライブラリをインストールして追加します。Arduino IDEのスケッチ欄からライブラリをインクルードを選択するとライブラリを管理の項目が表示されるのでクリックしてライブラリマネージャに遷移します。

ライブラリマネージャの検索欄にADXLを入力すると候補としてADXLに関するライブラリが表示されます。「Accelerometer ADXL345」を選択してインストールします。

ADXL345のライブラリの関数

ADXL345のライブラリをインストールするとADXL345のスケッチ例としてADXL345_demo_codeが準備されます。このスケッチ例を引用しながら初期化処理と加速度センサーの情報を取得します。設定する項目について使用しているライブラリについては以下の通りです。

関数設定するレジスタ内容
powerOn()POWER_CTL(0x2d)レジスタをクリアした後、AUTO_SLEEPとMeasureのビットをセットして測定モードにする。
setActivityTheshold(引数)THRESH_ACT(0x24)引数は符号なしの8ビットを値を指定する。スケールは62.5mg/LSB。アクティブイベントでの加速度信号の絶対値を比較される。
setInactivityThreshold(引数)THRESH_INACT(0x25)引数は符号なしの8ビットの値を指定する。スケールは62.5mg/LSB。インアクティブイベントでの加速度信号の絶対値と比較される。
setTimeInactivity(引数)TIME_INACT(0x26)引数は符号なしの8ビットの値を指定する。スケールは1sec/LSB。加速度がTHRESH_INACT値を下回ったまま設定した時刻が経過するとインアクティブとなる。
setActivityX(引数)
setActivityY(引数)
setActivityZ(引数)

setInactivityX(引数)
setInactivityY(引数)
setInactivityZ(引数)
ACT_INACT_CTL(0x27)引数に1または0を指定する。0を指定するとDCカップリング動作となり1を設定するとACカップリング動作が有効になります。
DCカップリング動作は現在の加速度の大きさをTHRESH_ACT及びTHRESH_INACTと比較して判定する。ACカップリング動作はアクティブ開始時の加速度を基準としてTHRESH_ACT値を上回るとアクティブをトリガする。THRESH_INACTは基準値を下回ったまま時間経過でインアクティブ判定となる。
setTapDetectionOnX(引数)
setTapDetectionOnY(引数)
setTapDetectionOnZ(引数)
TAP_AXES(0x2A)引数に1または0を指定する。タップ検出したい軸に対して1を指定するとタップ検出の対象となる。
setTapThreshold(引数)THRESH_TAP(0x1D)引数は符号なしの8ビットの値を指定する。タップ検出するための閾値を設定する。最大g値が+16gの時のスケールは62.5mg/LSB。
setTapDuration(引数)DUR(0x21)引数は符号なしの8ビットの値を指定する。スケールは625us/LSB。タップイベントの検出時に加速度信号がTHRESH_TAP閾値を超える最大時間を設定する。
setDoubleTapLatency(引数)LATENT(0x22)引数は符号なしの8ビットの値を指定する。スケールは1.25ms/LSB。タップイベントが検出されてから時間ウィンドウの開始までの待ち時間を設定する。
setDoubleTapWindow(引数)WINDOW(0x23)引数は符号なしの8ビットの値を指定する。スケールは1.25ms/LSB。ダブルタップ検出において待ち時間満了後2番目の有効なタップを検出できる時間幅を設定する。
setFreeFallThreshold(引数)THRESH_FF(0x28)引数は符号なしの8ビットの値を指定する。自由落下イベントを判定するためすべての軸の加速度とレジスタ値を比較する。スケールは62.5mg/LSB。推奨値は300~600ms(0x05~0x09)。
setFreeFallDuration(引数)TIME_FF(0x29)引数は符号なしの8ビットの値を指定する。スケールは5ms/LSB。全ての軸がTHRESH_FFの値を下回ったまま本レジスタの時間が経過すると自由落下割り込みとなる。推奨値は100~350ms(0x14~0x46)。
setInterruptMapping(引数1,引数2)INT_MAP(0x2F)引数1は対応するビット番号、引数2は割り込みの対象とするINT番号。引数1で指定したビット番号に対して0を指定するとINT1の出力、1を指定するとINT2の出力となる。
setInterrupt(引数1,引数2)INT_ENABLE(0x2E)引数1は対応するビット番号、引数2は割り込み対象のビットの割り込みを有効の有無。
getAcceleration(引数)DATAX0(0x32)~DATAZ1(0x37)引数は計算後の加速度情報を格納したい変数。小数点以下がでるためdouble型でXYZのデータを格納する配列が必要。
setFullResBit(引数)DATA_FORMAT(0x31)引数にレンジ範囲である2,4,8,16を指定する。
setFullResBit(引数)DATA_FORMAT(0x31)引数に1を指定すると最大分解能モード、0を指定すると10ビット固定モードとなる。
ADXL345のライブラリで使用する関数

ADXL345について詳細なデータを把握したい場合はデータシートを下記リンクからダウンロードしてください。

アナログデバイセズ:MEMS加速度センサーADXL345

ライブラリの使用例

#include <ADXL345.h>

#define INT_SINGLE_TAP_BIT_MASK 0x40

ADXL345 adxl;
byte intsrc;    //割り込みイベント
intsrc = adxl.getInterruptSource(); //割り込む要因を読み込む

if( intsrc & INT_SINGLE_TAP_BIT_MASK){ //割り込み要因のビットを確認
   Serial.println("SINGLE_TAP");
}

ADXL345.hをインクルードします。ADXL345クラスの型の変数を宣言し各種メソッドを呼び出して使用します。割り込み要因をgetInterruptSource()メソッドで読み込むとINT_SOURCEレジスタのデータが取得できます。

レジスタのデータは1バイトデータなので割り込み要因を特定するために論理積を取って対象のビットが立っているか確認します。

/* 加速度センサーのデータを取得 */
void Adxl345Rcv(){
  double xyz[3];
  double ax,ay,az;
  
  adxl.getAcceleration(xyz); //XYZの加速度の取得
  ax = xyz[0];
  ay = xyz[1];
  az = xyz[2];

  Serial.print("X:");
  Serial.print(ax);
  Serial.print(",");
  Serial.print("Y:");
  Serial.print(ay);
  Serial.print(",");
  Serial.print("Z:");
  Serial.print(az);
  Serial.println("");
}

getAcceleration()メソッドを使用するとXYZ軸の加速度情報を取得することができます。ADXL345の出力データレートはデフォルトで100Hz(10ms)なのでgetAcceleration()メソッドを使用する場合は10ms以下の読み込みにならないようにする必要があります。

BW_RATEレジスタのRateビットを変更することで出力データレートを変更することができますが、データの読み出しがデータレジスタの更新速度に追いつかなくなることがあるので注意が必要です。

割り込みを使用してイベントを管理する

volatile bool intboo1 = false; //最適化されないようにしている

void setup() {
  pinMode(35,INPUT_PULLDOWN);
  attachInterrupt(35,irt1,RISING); //35ピンが立上り変化で割り込み発生
}

/* DIが立上り変化で割込み処理 */
void IRAM_ATTR irt1(void){
    intboo1 = true;
}

割り込み内で使用する変数はソースコード全体で使用するためグローバル変数で宣言する必要があります。また変数を割り込み内のみでなくメインループ内で変更する場合はvolatile宣言して最適化されないようにする必要があります。

pinMode()で対象のピンを入力設定しattachInterrupt()で割り込みの設定を行います。 attachInterrupt() の第1引数には対象となるDIピンを指定します。第2引数には割り込み条件となった時にコールする関数を指定します。第3引数に割り込み発生の要因を指定します。

割り込み要因 内容
RISINGDIがLOWからHIGHに変化した時に通知
FALLINGDIがHIGHからLOWに変化した時に通知
CHANGEDIが変化した時通知
良く使用する割り込み発生のタイミング

上記の表以外にもONLOW/ONLOW_WE(DIがLOWの時発生でどちらも同じ動き)やONHIGH/ONHIGH_WE(DIがHIGHの時発生でどちらも同じ動き)がありますが使用することがないと思います。

割り込み発生時にコールする関数としてirt1()を実装します。voidの後ろに記述しているIRAM_ATTRはRAM領域に配置したい場合に指定します。指定しない場合はROM領域(フラッシュメモリ)に配置されます。

RAM領域に配置することでフラッシュメモリよりも高速に処理することができるため割り込み関数を少しでも高速にしたい場合に指定するとよいでしょう。

動作確認

動作確認用の回路図
動作確認用の回路図

ESP32-WROOM-32EのDIを使ってADXL345のINT1の割り込みを判定できるようにしています。Wireを使用する場合マイコン内蔵のプルアップ抵抗などがない場合は抵抗を実装する必要があります。ADXL345モジュールはプルアップ抵抗が実装されているので必要ありません。

Arduino IDEのシリアルモニタにはXYZの軸の加速度の表示するようにしています。10msのソフトウェアタイマを実装して10ms経過して加速度情報を取得するようにします。シリアルモニタとシリアルプロッタで動作確認を行いました。

動作確認(シリアルモニタでの確認)
動作確認(シリアルモニタでの確認)

Arduino IDEのシリアルプロッタを使用して加速度の変化が分かるように表示しました。シリアルモニタとシリアルプロッタは同時には使用できないためシリアルプロッタを使用する場合はソースコード全体の#define MONITER_USEをコメントアウトすると表示できます。

動作確認(シリアルプロッタでの確認)
動作確認(シリアルプロッタでの確認)

シリアルプロッタによる結果を確認すると加速度センサーを動かしているとXYZ軸の値が変化していることが分かります。机において動かさないようにすると加速度の動きが安定していることからうまく検出できていることが分かりました。

割り込みイベントでFREE_FALL(自由落下)を発生させようとするとXYZ軸すべての条件を満たす必要があるため頻繁に発生させることができませんでしたが、手に乗せたモジュールを軽く手の上で5cm程度投げると発生しました。

ソースコード全体

以下のソースコードはコンパイルして動作確認をしております。チェックはしておりますがコメントなど細かな部分で間違っている可能性があります。参考としてお使いいただければと思います。

#include <Wire.h>
#include <ADXL345.h>

#define TIME_OFF -1 //タイマーを使用しない場合
#define TIME_UP 0 //タイムアップ
#define BASE_CNT 10 //ベースタイマカウント値
#define TIME_ADXL_MAX 1

#define INT_SINGLE_TAP_BIT_MASK 0x40
#define INT_DOUBLE_TAP_BIT_MASK 0x20
#define INT_ACTIVITY_BIT_MASK   0x10
#define INT_INACTIVITY_BIT_MASK 0x08
#define INT_FREE_FALL_BIT_MASK  0x04
#define INT_WATERMARK_BIT_MASK  0x02
#define INT_OVERRUNY_BIT_MASK   0x01

#define PIN_DI1 35
//#define MONITER_USE //シリアルプロッタを使用する場合はコメントアウト

/* 変数宣言 */
uint32_t beforetimCnt = millis();
ADXL345 adxl;
byte intsrc;    //割り込みイベント
byte intsrc2;   //アクティブまたはタップイベントの内容
byte int2src;
int16_t  timAdxl345get;
volatile bool intboo1 = false;

/* プロトタイプ宣言 */
void mainApp(void);
void InitAdxl345();
void Adxl345Rcv();
void IRAM_ATTR irt1(void);

void setup() {

  Serial.begin(115200);
  pinMode(PIN_DI1,INPUT_PULLDOWN);
  attachInterrupt(PIN_DI1,irt1,RISING);
  Wire.begin();
  InitAdxl345();
}

void loop() {

  mainTimer();
  mainApp();
}
/* タイマ管理 */
void mainTimer(void){

  if ( millis() - beforetimCnt >= BASE_CNT ){
    beforetimCnt = millis();

    if( timAdxl345get > TIME_UP ){
      --timAdxl345get;
    }
  }
}
/* DIがRISINGして割込み処理 */
void IRAM_ATTR irt1(void){
    intboo1 = true;
}
/* メイン処理関数 */
void mainApp(void){
    
  if( intboo1 ){ //INT1
    intboo1 = false;
    intsrc = adxl.getInterruptSource();
    #ifdef MONITER_USE
      if( intsrc & INT_SINGLE_TAP_BIT_MASK){
        Serial.println("SINGLE_TAP");
      }
      if( intsrc & INT_DOUBLE_TAP_BIT_MASK){
        Serial.println("DOUBLE_TAP");
      }
      if( intsrc & INT_ACTIVITY_BIT_MASK){
        Serial.println("Activity");
      }
      if( intsrc & INT_INACTIVITY_BIT_MASK){
        Serial.println("inactivity");
      }
      if( intsrc & INT_FREE_FALL_BIT_MASK){
        Serial.println("FREE_FALL");
      }
      if( adxl.isActivitySourceOnX()){
        Serial.println("ACT X");
      }
      if( adxl.isActivitySourceOnY()){
        Serial.println("ACT Y");
      }
      if( adxl.isActivitySourceOnZ()){
        Serial.println("ACT Z");
      }
      if(adxl.isTapSourceOnX()){
        Serial.println("TAP X");
      }        
      if(adxl.isTapSourceOnY()){
        Serial.println("TAP Y");
      } 
      if(adxl.isTapSourceOnZ()){
        Serial.println("TAP Z");
      }
    #endif
  }

  if( timAdxl345get == TIME_UP ){
    timAdxl345get = TIME_ADXL_MAX;
    Adxl345Rcv();
  }
}
/* 加速度センサーのデータを取得 */
void Adxl345Rcv(){
  double xyz[3];
  double ax,ay,az;
  
  adxl.getAcceleration(xyz); //XYZの加速度の取得
	ax = xyz[0];
	ay = xyz[1];
	az = xyz[2];

  #ifndef  MONITER_USE
    Serial.print("X:");
    Serial.print(ax);
    Serial.print(",");
    Serial.print("Y:");
    Serial.print(ay);
    Serial.print(",");
    Serial.print("Z:");
    Serial.print(az);
    Serial.println("");
  #endif
}
/* 加速度センサーの初期化 */
void InitAdxl345(){
    byte dmy;

    adxl.powerOn();

    //set activity/ inactivity thresholds (0-255)
    adxl.setActivityThreshold(75); //62.5mg per increment 75
    adxl.setInactivityThreshold(75); //62.5mg per increment
    adxl.setTimeInactivity(10); // how many seconds of no activity is inactive?
 
    //look of activity movement on this axes - 1 == on; 0 == off 
    adxl.setActivityX(1);
    adxl.setActivityY(1);
    adxl.setActivityZ(1);
 
    //look of inactivity movement on this axes - 1 == on; 0 == off
    adxl.setInactivityX(1);
    adxl.setInactivityY(1);
    adxl.setInactivityZ(1);
 
    //look of tap movement on this axes - 1 == on; 0 == off
    adxl.setTapDetectionOnX(0);//0
    adxl.setTapDetectionOnY(0);//0
    adxl.setTapDetectionOnZ(1);//1
 
    //set values for what is a tap, and what is a double tap (0-255)
    adxl.setTapThreshold(50); //62.5mg per increment
    adxl.setTapDuration(15); //625us per increment
    adxl.setDoubleTapLatency(80); //1.25ms per increment
    adxl.setDoubleTapWindow(200); //1.25ms per increment
 
    //set values for what is considered freefall (0-255)
    adxl.setFreeFallThreshold(7); //(5 - 9) recommended - 62.5mg per increment
    adxl.setFreeFallDuration(45); //(20 - 70) recommended - 5ms per increment 45

    adxl.setFullResBit(1); //最大分解能モード
    adxl.setRangeSetting(4); //4g Range 2,4,8,16を指定
    //adxl.setFiFobyte(0x9F);

    //setting all interrupts to take place on int pin 1
    //I had issues with int pin 2, was unable to reset it
    //adxl.setInterruptMapping( ADXL345_DATA_READY,   ADXL345_INT2_PIN );
    adxl.setInterruptMapping( ADXL345_INT_SINGLE_TAP_BIT,   ADXL345_INT1_PIN );
    adxl.setInterruptMapping( ADXL345_INT_DOUBLE_TAP_BIT,   ADXL345_INT1_PIN );
    adxl.setInterruptMapping( ADXL345_INT_FREE_FALL_BIT,    ADXL345_INT1_PIN );
    adxl.setInterruptMapping( ADXL345_INT_ACTIVITY_BIT,     ADXL345_INT1_PIN );
    adxl.setInterruptMapping( ADXL345_INT_INACTIVITY_BIT,   ADXL345_INT1_PIN );
 
    //register interrupt actions - 1 == on; 0 == off  
    //adxl.setInterrupt( ADXL345_DATA_READY, 1);
    adxl.setInterrupt( ADXL345_INT_SINGLE_TAP_BIT, 1);
    adxl.setInterrupt( ADXL345_INT_DOUBLE_TAP_BIT, 1);
    adxl.setInterrupt( ADXL345_INT_FREE_FALL_BIT,  1);
    adxl.setInterrupt( ADXL345_INT_ACTIVITY_BIT,   1);
    adxl.setInterrupt( ADXL345_INT_INACTIVITY_BIT, 1);

    dmy = adxl.getInterruptSource(); //ダミーで割り込みビットをクリア
}

関連リンク

Arduinoのライブラリを使って動作確認を行ったことを下記リンクにまとめています。

Arduinoで学べるマイコンのソフト開発と標準ライブラリの使い方

Seeeduino XIAOで学べるソフト開発と標準ライブラリの使い方

ESP32-WROOM-32Eで学べるソフト開発と標準ライブラリの使い方

Code Camp完全オンラインのプログラミング個人レッスン【無料体験】

最後まで、読んでいただきありがとうございました。

タイトルとURLをコピーしました