生産現場IoTへの挑戦 #10 ～Raspberry Pi 3A+で集めたデータをCSV出力するサーバを作る～

# 1.はじめに
やりたいのは、M5Stackなどで採取したデータをCSVファイルにしてネットワーク上から閲覧できる様にすること。
データ量はそれほど多くない前提でDBなども使わず簡便なシステムを作ってみた。

# 2.環境づくり
Raspberry Pi はこんな感じで各種ツールなどを入れて環境を作りました。

## 2.1.Raspberry pi OS
Rspberry Pi Imagerで起動用のMicro SDカードを作ります。
OSには、Raspberry Pi OS Lite 32Bit (bullseye)を使用しました。

3A+ はUSB OTGが使えないのですが、Zero2や4Bの場合次の操作をしてUSBケーブル接続でSSHログインできる様にしておくと便利です。

SDカードのbootフォルダ（Windowsからアクセスできるドライブ）の以下のファイルを書き換えます。
### comdline.txt
rootwaitの後にmodules-load=dwc2,g_etherを追加。スペース区切り。

### config.txt
末尾にdtoverlay=dwc2を追加。

Raspberry Piを起動したら、
```shell
sudo apt update
sudo apt -y upgrade
```
## 2.2.SAMBAサーバーをインストールしてファイル共有する
Node-REDでCSV化したファイルにネットワーク内のPCからアクセスするため、共有用のフォルダを作成します。
```shell
sudo apt install samba
sudo mkdir /home/Share
sudo chmod 777 /home/Share
```
sambaの共有設定を行います。
```shell
sudo nano /etc/samba/smb.conf
```
ファイルの最後に共有設定を追記します。
```
[share]
comment = Share
path = /home/Share
public = yes
read only = no
browsable = yes
force user = ****
```
force userには自分が作ったraspberry piのユーザーを入れるといいでしょう。最近はpiユーザー作らないのが推奨ですので。

## 2.3.Node-REDのインストール
Node-REDは[このサイト](https://nodered.jp/docs/getting-started/raspberrypi)の通りインストールします。
合せて自動起動も設定。
```shell
bash <(curl -sL https://raw.githubusercontent.com/node-red/linux-installers/master/deb/update-nodejs-and-nodered)
node-red-pi --max-old-space-size=256
sudo systemctl enable nodered.service
```
センサ端末との通信に使用するMQTTブローカーもNode-RED上にインストールしておきます。
右上の三本線のアイコンから設定を開き、「パレットの管理」
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/496179/1ef369ca-2069-40aa-a199-bf7c23d13f83.png)

「ノードを追加」を選んで「aedes」で検索。「node-red-contrib-aedes」をインストールします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/496179/67f31758-fcee-02a0-b9fa-51e3fc80ea26.png)

適当な端末のブラウザでNode-REDを開いて、次の手順でノードをインストールします。
Node-REDを開くには、[Node-REDのIPアドレス]:1880にアクセスします。

# 3.Node-REDでコーディング
Node-REDでコーディングします。
大まかな流れは、M5Stackを想定したデバイスからJsonに成型したデータをMQTTで受け取り、CSVに変換して共有フォルダに保存します。
ここではダミーのデータを使ってデモを動かします。

## 3.1.MQTTブローカを設置
「aedes broker」ノードを引き出してデプロイするだけです。ポートは標準の1883番を使っています。超簡単ですね。 
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/496179/375edd80-5b0a-d99e-d4da-3baa7c38efbc.png)

## 3.2.ダミーデータ送信フロー
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/496179/5550cc16-2a5e-2aba-e554-dad6cfe9dd9e.png)
全体像はこんな感じです。「タイムスタンプ」とあるのは　「injection」ノードです。ノードの左側のボタンを押すと時間データを出力します。「dummy data」は「function」ノードです。タイムスタンプから届いた時間データをunix時間からyyyy/mm/dd hh:MM:ssに変換し、ダミーデータとして、気温、湿度、時間データを追加したオブジェクトを生成します。
「json」ノードはオブジェクトをjsonに変換しています。「mqtt out」ノードは届いたデータをMQTTで送信します。送信先は「AedesMQTTbroker」の「env data」トピックです。 

各ノードの設定は次の通りです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/496179/72a9c83c-050f-75e8-1059-85c3dcd412db.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/496179/9522e9d7-7381-57ee-1545-9b3de68ed042.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/496179/eea2cd27-aa36-9892-4d4f-e8658af23aa3.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/496179/01fe1943-4f6a-233c-1487-0300e55e1e68.png)

## 3.3.データ受信からCSVファイル保存まで
CSVファイルの保存は「mqtt in」以降の三つのノードで実装できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/496179/856fb5b9-7b44-7a90-a034-1ca1f0d1081a.png)
「mqtt in」ノードでは、「env data」トピックのデータを受信します。「csv」ノードはオブジェクトをカンマ区切りのテキストに変換します。「write file」ノードは、新しいデータが届くたびに指定のファイルに追記します。ここでは、先ほど作成した共有フォルダ「/home/Share」に「test.csv」という名前のファイルで保存しています。csvファイルが無い場合は新規作成、有る場合は追記してくれます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/496179/e57c871b-2ae9-bd99-8e87-bb2f03c94d4a.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/496179/c94cd45f-b304-1c69-4e75-28eb2fcf674a.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/496179/5ac43a6c-4a37-b5f1-2a1f-d8be547d8ff5.png)

ポイントとしては、センサのデータを次のようなjson形式にして送信しておくことです。この状態であれば、CSVにパースして保存するのも非常に簡単です。
```json
{
    "title1"  : "data1",
    "title2"  : "data2",
    "title3"  : "data3",
    .....
}
```
今回は割愛しますが、CSVのファイル名を「yyyymmdd.csv」の様にして毎日分割したい。とか、最上段にはデータのタイトル行を入れたい。とかも、functionノードを使えばよしなにできます。

# 4.microSDのバックアップを取る
ラズパイの最大の弱点はMicroSDです。運用のための設定ができたので、バックアップを取っておきましょう。
Raspberry Pi OSのGUI版であればメニューにSDカードのコピー機能がありますが、今回はCUIオンリーのLite版を使用していますのでバックアップツールをインストールしてバックアップします。

方法はこちらのサイトを参考にさせていただきました。
https://penguinlabo.hatenablog.com/entry/raspberrypi-backup
```
sudo apt install -y git
git clone https://github.com/billw2/rpi-clone.git
cd rpi-clone
sudo cp rpi-clone rpi-clone-setup /usr/local/sbin
```

これでバックアップツールが入りました。
USBポートにMicroSDリーダーとバックアップ先にするMicroSDカードを挿入してリブートします。

```
sudo rpi-clone sda
```
クローンしていいか聞かれるのでyes、ラベル名を聞かれるので16文字以内で入力してエンターするとバックアップが開始されます。
データ量が少ないためか、3分強で終了しました。

今回の用途では、書き換えられるデータはすべて共有フォルダに入っているため、データのバックアップはネットワーク上で適当なツールを使用するのが良いかと思います。MicroSDのイメージを定期的にバックアップしたい場合は参考元のサイトの通りcrontabでrpi-cloneを使うと、差分バックアップしてくれるみたいですね。

# 5.まとめ
お仕事でもRaspberry Pi 4Bを使ってデータ収集することがあります。当初が頑張ってデータベースを入れたりしてたのですが、データベースのデータを使える人が少ない（ほぼいない）のでCSVでデータが欲しいというニーズがありました。
また、Raspberry Pi 4Bよりも3A+の方が若干入手しやすくなってきたという環境もあり、今回のシステムを作ってみました。
作ってる途中で気づいたのですが、3A+はUSB OTGに対応してなくてUSB SSH接続ができませんでした。ヘッドレス運用が基本なので、Wi-Fi環境が変わると接続できなくなってしまいちょっと困った事態になってしまいました。
性能はZero 2Wとほぼ同じなので、Zero2Wの供給さえ安定すればOTGのできるZero2Wを選定することになりそうです。