M5Stackで部屋の震度を時系列に計測した

なんだか部屋が揺れてる気がする...
![jishin_kinishinai_people.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16457/eb6eefaa-e0bd-20dd-1f9f-873c51b4ba64.png)

ということで、まずはスマホを置いて加速度センサーで揺れ具合を確認してみました。
その結果、データ上でも反応があり、気のせいではないことが確定。
ただ、揺れが気になる時間と気にならない時間もありそうな気配でした。
時間変化の状況を確認したいので長期的に計測したいと考えたけど、スマホを放置して常にセンシングすると日常が困る。
というわけで、代わりに持ってたM5Stackを使うことにしました。

# M5Stackの設置
空気清浄機の上にこんな風に設置しました。
![m5stack_setting.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16457/22c5a9a0-44bc-f132-0d19-b3b10f4d408f.jpeg)

# データ計測の構成
M5Stackだけで長時間データが収集できないので、定期的にサーバーにデータ送信することにしました。
今はIoTサービスも色々ありますが、レンタルサーバーがすでにあったのでデータの受信・蓄積を自作することにしました。
安価なレンタルサーバーでもとりあえずさくっと動かせるのでPHPでデータを受け取ることにし、今回はリアルタイムに結果を見る必要はなかったので、時々手動FTPでローカルPCにダウンロードして、Pythonで集計・グラフ化しました。

というわけで、こんな感じの構成でデータ計測しました。
![m5stack_sysytem.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16457/438ebea3-cca3-3ac9-41af-70caae83d40c.png)

# どんなデータが取れたか
取得データをグラフ化すると、某日はこんな感じでした。
![graph.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16457/1238f2bb-3f0f-fdf4-cbb9-752de5c4e3c9.png)
この日のデータに限らず、震度が急に増加してしばらく続き、急に震度が下がる傾向があることがわかりました。
どうやら何か機械が動いたり止まったりしている影響を受けている気配です。

他にも、このグラフ上では17時頃に急にデータがとびぬけてますが、その時間は子供がセンサーのある部屋でドタバタ遊んでいました。
あとは、誰かがうっかりぶつかったり、部屋の掃除で物が動いたりしてる時間も反応が大きく出ます。
後からデータを見直すと、この時間は子供がこの部屋を使ってたっぽい、とか、この時間はたぶん掃除をしてたな、とかも推測できたりして、面白いデータが取れました。
計測期間中に一度地震があり、自分の地域ではあまり揺れは大きくなかったですが、その際もはっきりデータで捉えることに成功しました。

データ計測は1か月間続けていて、計測し続けてることでわかったこともあったのですが、具体的な分析はこの記事の対象外なので割愛します。

# センシング（M5Stack）
加速度データは最終的にcsv形式で扱うようにしました。
ただ、M5Stackの時点でcsvの文字列データにすると通信データ量が増えそうなので、M5Stackからはfloatのデータのままサーバーへ通信しました。
なお、このコードの場合、2～3分間データを貯めた後にサーバーに送信しますが、データ送信のタイミングで数秒程度処理が止まります。
M5Stack内臓のESP32はマルチタスクが可能なので、データ送信中もセンシングし続けることもできたかもしれません。
今回は、1日の時間的傾向が見られれば良かったので2～3分ごとに数秒程度データが抜けても特に問題く、気にしないことにしました。

```c++:m5stack
#define M5STACK_MPU6886 

#include <M5Stack.h>
#include <WiFi.h>
#include <HTTPClient.h>

const char SSID[] = "○○○○○";       //WiFi SSID
const char PASSWORD[] = "●●●●●";    //WiFi パスワード

float accX = 0.0F;
float accY = 0.0F;
float accZ = 0.0F;

const int maxStockLength = 3500; //貯める加速度データ数。あまり大きくするとビルドできなかった
int stockCount=0;
float accDataLists[maxStockLength*3];//x,y,zの3つのデータがあるので3倍する

HTTPClient http;
const char *host = "http://example.com/datachecker/"; //サーバーのAPIのパス

bool isDataSend=false;
String serverResponseTime;

void setup(){
  M5.begin();
  M5.Power.begin();
  M5.IMU.Init();

  M5.Lcd.fillScreen(BLACK);
  M5.Lcd.setTextColor(GREEN , BLACK);
  M5.Lcd.setTextSize(2);

  //wifiに接続する
  setupNetwork();
   
}

void loop() {
  M5.update();

  //加速度データの取得
  M5.IMU.getAccelData(&accX,&accY,&accZ);

  M5.Lcd.setCursor(0, 0);
  if(isDataSend){
    M5.Lcd.print("data send -> active      ");
  }else{
    M5.Lcd.print("data send -> not active   ");
  }

  M5.Lcd.setCursor(0, 35);
  M5.Lcd.print(serverResponseTime); //サーバーから帰ってくる最終データ通信時刻を表示する
  M5.Lcd.setCursor(0, 65);
  M5.Lcd.printf(" %5.2f   %5.2f   %5.2f   ", accX, accY, accZ);
  M5.Lcd.setCursor(220, 87);
  M5.Lcd.print(" G");
  M5.Lcd.setCursor(220, 120);
  M5.Lcd.printf(" %d   ", stockCount);

  //加速度データを貯める
  accDataLists[stockCount*3]=accX;
  accDataLists[stockCount*3+1]=accY;
  accDataLists[stockCount*3+2]=accZ;

  //貯めるデータカウントをインクリメント。最大数まで溜まったらサーバーへ送信する
  stockCount++;
  if(stockCount>=maxStockLength){
    sendData();
    stockCount=0;
  }

  //送信テスト用
  if (M5.BtnA.wasPressed()) {
    sendData();
  }

  //データをサーバーに通信するかどうかを実行中にボタンで変更できるようにする
  if (M5.BtnC.wasPressed()) {
    isDataSend=!isDataSend;
    if(isDataSend){
        allReset();
    }
  }

  delay(5);
}

//貯めたデータをクリアする
void allReset(){
  for(int i =0;i<maxStockLength;i++){
    accDataLists[i]=0;
    accDataLists[i+maxStockLength]=0;
    accDataLists[i+maxStockLength*2]=0;
  }
  stockCount=0;
}

//wifiに接続する
void setupNetwork(){
  WiFi.begin(SSID, PASSWORD);
  M5.Lcd.print("WiFi connecting ");
  M5.Lcd.print(SSID);

  //接続をまつ
  while (WiFi.status() != WL_CONNECTED) {
    M5.Lcd.print(".");
    delay(300);
  }
  M5.Lcd.print("WiFi connected!");
}

//サーバーに送信する。Stringにするとデータ量が増えるので、uint8_tに変換して投げた。
void sendData(){
  int dataSize=sizeof(accDataLists);
  uint8_t *dataArray = reinterpret_cast<uint8_t*>(&accDataLists);
  http.begin(host);
  int status_code = http.POST(dataArray,dataSize);
　//サーバーからデータ通信時刻の文字列を返すようにしているので、ここで受け取る
  serverResponseTime = http.getString();
  http.end();
}


```

# データ受信・保存（PHP）
サーバー側では、1日辺り1つのログファイルが作られるようにしました。
PHPでは、`file_get_contents('php://input')` とすると受信データをそのまま扱えるので、これでM5Stackからのデータをまるっと受け取って`unpack()`でfloatに変換、その後csv文字列に変換して保存しました。
今回のログデータの場合は、1日辺りの約90MBほどのデータサイズになりました。
なお、自分だけがこっそり使うものだったので、データチェックは甘いです。

```php:サーバー
<?php

//ログ保存用ディレクトリ。なければ作る。
$lodDir="log/";
if(!file_exists($lodDir)){
    mkdir($lodDir);
}

//バイナリデータを受け取る
$bindata = file_get_contents('php://input');

//バイナリデータをfloatとしてパースする。M5StackとPHPのfloatは少数桁数が違うので解析時に注意する（後述参照）。
$unpacked = unpack('f*', $bindata);

//データが空かどうかくらいはチェックしておいた
if(empty($unpacked)){
    echo "no data";
}else{
    //現在時を取得
    $nowDateTime = new DateTime();

    //現在日をファイル名に設定
    $fileName=$nowDateTime->format('Y-m-d').".csv";

    //データ受信時間を受け取ってcsvデータにする
    $dateTimeStr = $nowDateTime->format('Y-m-d H:i:s');
    $dataStr=$dateTimeStr .",".implode(",",$unpacked)."\n";

    //追加書き込みでデータを書き込む
    file_put_contents($lodDir.$fileName,$dataStr,FILE_APPEND);
    
    //データ受信時間を返す。M5Stack表示用
    echo $dateTimeStr;
}
?>
```

# 集計してグラフ描画

上記コードの組み合わせで、こんな感じのデータが記録されます。
x,y,z,x,y,z...という順番でデータが並んでます。

```
2021-10-16 00:00:59,0.002197265625,0.030029296875,1.071533203125,0.005615234375,0.033447265625,1.0634765625,-0.00537109375,0.031982421875,1.0751953125,0.009521484375,0.026123046875,1.076171875,0.001220703125,0.025634765625,1.06201171875,-0.0009765625,0.0302734375,1.068115234375,0.003662109375,0.028564453125,1.07763671875,0.011474609375,0.021484375,1.073974609375,0.00732421875,0.02978515625,1.073486328125,0.003662109375,0.031494140625,1.07177734375,0.008056640625,0.027587890625,1.07470703125,0.00390625,0.03076171875,1.0673828125,0.002197265625,0.03173828125,1.06689453125,0.00048828125,0.02685546875,1.064453125,0.00439453125,0.02685546875,1.071533203125,0.002685546875,0.01904296875,1.073974609375,0.002197265625,0.02587890625,1.07177734375,0.00927734375,0.024658203125,1.071044921875,-0.005859375,0.0263671875,1.07470703125,-0.004150390625,0.02978515625,1.064208984375,0.020751953125,0.032958984375,1.064697265625,-0.00146484375,0.017333984375,1.08349609375,-0.016845703125,0.032958984375,1.062255859375,0.021728515625,0.037109375,1.07275390625,0.0068359375,0.018798828125,1.07861328125,-0.014404296875,0.027587890625,1.06884765625,0.02099609375,0.0380859375,1.063232421875,0.012451171875,0.01806640625,1.06640625,-0.015625,0.028076171875,1.0703125,0.0166015625,0.026611328125,1.067626953125,0.009033203125,0.02197265625,1.07080078125,-0.01806640625,0.02294921875,1.07666015625,0.017578125,0.028076171875,1.070068359375,0.02783203125,0.024169921875,1.0703125,-0.021728515625,0.0234375,1.070068359375,0.00732421875,0.03173828125,1.06640625,0.029296875,0.0224609375,1.069580078125,-0.00927734375,0.024169921875,1.070556640625,-0.005859375,0.028564453125,1.07470703125,0.021484375,0.029052734375,1.072998046875,-0.004150390625,0.033447265625,1.066650390625,-0.007080078125,0.03076171875,1.0654296875,0.021728515625,0.033203125,1.069580078125,0.0078125,0.022216796875,1.07080078125,-0.013671875,0.023681640625,1.081298828125,0.01953125,0.0341796875,1.072021484375,0.008056640625,0.020751953125,1.070556640625,-0.0205078125,0.025390625,1.066162109375,0.011962890625,0.035400390625,1.07421875,0.015380859375,0.02587890625,1.06103515625,-0.018798828125,0.0302734375,1.069091796875,0.02197265625,0.03564453125,1.06298828125,0.0166015625,0.027099609375,1.068115234375,-0.0029296875,0.027587890625,1.062255859375,0.001220703125,0.035400390625,1.064208984375,0.016357421875,0.029296875,1.070556640625,-0.018798828125,0.025146484375,1.080078125,-0.00439453125,0.03564453125,1.0615234375,0.011474609375,0.038330078125,1.06201171875,-0.002685546875,0.029052734375,1.069580078125,0.000732421875,0.04052734375,1.068359375,0.009521484375,0.037109375,1.0732421875,0.0068359375,0.02880859375,1.061767578125,-0.011962890625,0.02978515625,1.067138671875,0.00830078125,0.02587890625,1.072509765625,0.005859375,0.02197265625,1.0712890625,-0.002685546875,0.037841796875,1.06591796875,0.010009765625,0.02783203125,1.0712890625,0.009765625,0.021484375,1.06787109375,-0.0146484375,0.030029296875,1.068603515625,0.0068359375,0.0390625,1.069580078125,0.00537109375,0.03076171875,1.06884765625,-0.001953125,0.025634765625,1.0732421875,0.005126953125,0.025634765625,1.06591796875,0.00634765625,0.025634765625,1.074462890625,-0.0048828125,0.03173828125,1.07373046875,0.005859375,0.030517578125,1.069091796875,0.002685546875,0.031982421875,1.064453125,0.00048828125,0.031494140625,1.064208984375,0.01220703125,0.03125,1.072021484375,0.002685546875,0.025634765625,1.0712890625,-0.000244140625,0.033447265625,1.075439453125,0.0126953125,0.02978515625,1.0732421875,0.000732421875,0.026611328125,1.076171875,-0.000244140625,0.03076171875,1.076416015625,0.009033203125,0.0322265625,1.064208984375,0.00537109375,0.023681640625,1.0732421875,-0.00732421875,0.0302734375,1.06298828125,0.008544921875,0.026123046875,1.077392578125,0.0048828125,0.028564453125,1.0576171875,-0.018310546875,0.028564453125,1.081298828125,0.004150390625,0.029052734375,1.071533203125,0.001953125,...
2021-10-16 00:03:16,0.00146484375,0.029541015625,1.07275390625,-0.00244140625,0.0263671875,1.079833984375,0.0234375,0.02880859375,1.068115234375,-0.002197265625,0.039794921875,1.07177734375,-0.0107421875,0.035400390625,1.07666015625,0.010498046875,0.035888671875,1.071533203125,0.010986328125,0.032470703125,1.06201171875,-0.009765625,0.022705078125,1.073486328125,0.00732421875,0.02685546875,1.07373046875,0.004638671875,0.029296875,1.06787109375,-0.0048828125,0.033203125,1.07568359375,0.0029296875,0.033447265625,1.070556640625,-0.00341796875,0.032470703125,1.0673828125,-0.00927734375,0.03076171875,1.064697265625,0.00537109375,0.03564453125,1.06884765625,0.010009765625,0.0263671875,1.075439453125,-0.002197265625,0.025390625,1.06982421875,0.010498046875,0.026611328125,1.070556640625,0.0048828125,0.037109375,1.066162109375,0.003662109375,0.033935546875,1.078857421875,0.006103515625,0.033447265625,1.064453125,0.003662109375,0.030029296875,1.0771484375,-0.007080078125,0.02587890625,1.073974609375,0.005126953125,0.0302734375,1.0634765625,-0.0048828125,0.028076171875,1.072509765625,0.008544921875,0.0283203125,1.0771484375,0.008544921875,0.031494140625,1.072021484375,0.008056640625,0.029541015625,1.05615234375,0.010498046875,0.03076171875,1.065673828125,0.005126953125,0.038818359375,1.06640625,0.00244140625,0.027587890625,1.064697265625,0.009521484375,0.033447265625,1.070556640625,-0.00146484375,0.024169921875,1.065185546875,0.0009765625,0.032958984375,1.074462890625,0.01025390625,0.029541015625,1.067626953125,0.003173828125,0.029052734375,1.074462890625,0.009033203125,0.030517578125,1.073486328125,0.001220703125,0.025146484375,1.07275390625,0.00048828125,0.0361328125,1.06298828125,0.006591796875,0.025146484375,1.075927734375,0.00048828125,0.033447265625,1.072509765625,-0.00439453125,0.02587890625,1.076171875,0.00732421875,0.0283203125,1.078857421875,0.00634765625,0.027099609375,1.068115234375,-0.0048828125,0.026123046875,1.0732421875,0.01025390625,0.0361328125,1.07080078125,-0.000244140625,0.027099609375,1.077880859375,-0.0078125,0.0341796875,1.064208984375,0.01220703125,0.032958984375,1.071044921875,0.005126953125,0.02978515625,1.064697265625,-0.001953125,0.017578125,1.057861328125,0.01025390625,0.0341796875,1.068115234375,0.004150390625,0.030517578125,1.060791015625,-0.0126953125,0.019287109375,1.064697265625,-0.00732421875,0.0322265625,1.06884765625,0.013916015625,0.021728515625,1.06884765625,-0.006591796875,0.0224609375,1.067138671875,0.0048828125,0.0322265625,1.06640625,0.018310546875,0.029052734375,1.060546875,-0.0009765625,0.035888671875,1.067626953125,0.002685546875,0.028076171875,1.072265625,0.012451171875,0.022216796875,1.07470703125,0.000732421875,0.027587890625,1.070068359375,-0.00048828125,0.02783203125,1.072265625,0.010009765625,0.033203125,1.067626953125,-0.00390625,0.029052734375,1.071044921875,0.003662109375,0.019287109375,1.065185546875,0.006591796875,0.03076171875,1.06982421875,0.01904296875,0.033203125,1.0712890625,0.001953125,0.0234375,1.0693359375,-0.00244140625,0.03369140625,1.065673828125,0.01025390625,0.034423828125,1.06982421875,0.00634765625,0.020263671875,1.070556640625,-0.014892578125,0.0361328125,1.06591796875,0.014404296875,0.023193359375,1.064208984375,0.000244140625,0.0263671875,1.070556640625,-0.00244140625,0.025390625,1.078857421875,0.01416015625,0.034423828125,1.063720703125,0.00146484375,0.021484375,1.075439453125,-0.009765625,0.0283203125,1.076904296875,0.010498046875,0.0322265625,1.07568359375,0.00732421875,0.025634765625,1.075927734375,-0.01318359375,0.02734375,1.072998046875,0.0185546875,0.036865234375,1.069580078125,0.01220703125,0.036376953125,1.076171875,-0.01416015625,0.024169921875,1.0693359375,0.013671875,0.04345703125,1.067626953125,0.012939453125,0.03125,1.076416015625,-0.017822265625,0.02734375,1.0703125,0.002197265625,0.0263671875,1.06005859375,0.02197265625,0.0283203125,1.059326171875,-0.009521484375,0.033203125,1.074951171875,...
...

```

FTPでデータダウンロードしてローカルPCにデータを移したら、あとはExcelでも、何かプログラムで解析するでも、やりやすいようにやればOKです。

今回はpythonのMatlibplotでグラフ描画しましたが、とりあえず状況が分かればいい程度の内容なのでコードは割愛します。
ただ、注意点が一つ。

M5Stackのfloatは小数点7桁までしか扱っておらず、PHPのfloatは小数点16桁までのため、PHPでunpackした際に小数点8桁以降で無意味な数値が入ってることがあります。
pythonだと、`dataNumberString`にセンシング数値の文字列があるとした場合、`round(float(dataNumberString),7)`で指定桁数のfloat値にできるので、これで調整して扱いました。

なお、pythonの`round()`の場合は偶数に丸めるため、正確性高く丸めたい場合は`decimal`モジュールを使うと良いらしいです。
参考：https://qiita.com/yoshi-kin/items/26e0cfc1c6768be2fc83

# まとめ
M5StackのセンシングデータをPHPで継続的に受け取りたい人がいる場合に参考になることもあるかもしれない、と思ってこの記事を書いてみました。

M5Stackの方は1か月継続計測を任務を終え、固定状態から解放していつもの電子工作遊びの道具へと戻っていきました。
持っててよかったM5Stack。