センサーデータをラズパイに保存！！(実装編)

# センサーデータをラズパイに保存！！(実装編)

ラズパイに保存するのに準備編で環境が整ったので実装に入ります。

## 目次

- ESP32 でのデータ送信
- データベース登録（ラズパイ）

## ESP32 でのデータ送信

センシングデータを送信します。

```c
#include <WiFi.h>
#include <PubSubClient.h>
#include <ArduinoJson.h>
#include <RTClook.h>
// WiFi
const char ssid[] = "ルーター";
const char passwd[] = "password";

// Pub/Sub
const char* mqttHost = "192.168.11.90"; // MQTTのIPかホスト名
const int mqttPort = 1883;       // MQTTのポート
char pubMessage[512];
WiFiClient wifiClient;
PubSubClient mqttClient(wifiClient);

const char* topic = "soil_value/SEN0193";     // 送信先のトピック名
char* payload;                   // 送信するデータ

int val;
///////////////////////////////////////////////////////////////////
String buildJson() {
  String json = "";
  const int capacity = JSON_OBJECT_SIZE(20);
  StaticJsonDocument<capacity> doc;
  DynamicJsonDocument logs(64);
  int val;
  val = analogRead(A6);
  Serial.println(val);
  doc["soil_value"] = val;

  serializeJson(doc, json);

  return json;
}

void setup() {
  Serial.begin(9600);

  // Connect WiFi
  connectWiFi();

  // Connect MQTT
  connectMqtt();
}

void loop() {

  // 送信処理 topic, payloadは適宜
  String jsonStr = buildJson();
  jsonStr.toCharArray(pubMessage, jsonStr.length() + 1);
  mqttClient.publish(topic, pubMessage);
  delay(1000);

  // WiFi
  if ( WiFi.status() == WL_DISCONNECTED ) {
    connectWiFi();
  }
  // MQTT
  if ( ! mqttClient.connected() ) {
    connectMqtt();
  }
  mqttClient.loop();

}

/**
   Connect WiFi
*/
void connectWiFi()
{
  WiFi.begin(ssid, passwd);
  Serial.print("WiFi connecting...");
  while (WiFi.status() != WL_CONNECTED) {
    Serial.print(".");
    delay(100);
  }
  Serial.print(" connected. ");
  Serial.println(WiFi.localIP());
}
/**
   Connect MQTT
*/
void connectMqtt()
{
  mqttClient.setServer(mqttHost, mqttPort);
  while ( ! mqttClient.connected() ) {
    Serial.println("Connecting to MQTT...");
    String clientId = "ESP32-" + String(random(0xffff), HEX);
    if ( mqttClient.connect(clientId.c_str()) ) {
      Serial.println("connected");
    }
    delay(1000);
    randomSeed(micros());
  }
}
```

### コード解説

 このメソッドは、Wi-Fi に接続してくれます。
 loop でも、接続されていない場合は、これで接続されます。

 ```c
 void connectWiFi()
 {
  WiFi.begin(ssid, passwd);
  Serial.print("WiFi connecting...");
  while (WiFi.status() != WL_CONNECTED) {
    Serial.print(".");
    delay(100);
  }
  Serial.print(" connected. ");
  Serial.println(WiFi.localIP());
 }
 ```

 このメソッドは、Mqtt に接続してくれます。
 loop でも、接続を維持してくれます。

 ```c
 void connectMqtt()
 {
  mqttClient.setServer(mqttHost, mqttPort);
  while ( ! mqttClient.connected() ) {
    Serial.println("Connecting to MQTT...");
    String clientId = "ESP32-" + String(random(0xffff), HEX);
    if ( mqttClient.connect(clientId.c_str()) ) {
      Serial.println("connected");
    }
    delay(1000);
    randomSeed(micros());
  }
 }
 ```

 buildJson は、Json 形式で送信できます。
 これは、複数センサーを組み合わせた際、Json で送信することで受信側での判別のしやすさを意識してこう言った形式にしています。

 ```c
 String buildJson() {
  String json = "";
  const int capacity = JSON_OBJECT_SIZE(20);
  StaticJsonDocument<capacity> doc;
  DynamicJsonDocument logs(64);
  int val;
  val = analogRead(A6);
  Serial.println(val);
  doc["soil_value"] = val;

  serializeJson(doc, json);

  return json;
 }
 ```

## データベース登録（ラズパイ）

python で簡単に書いたものが以下です。
ちなみに、mqtt を簡単に扱えるように独自にライブラリを作成していますが、
引数では、topic、IP、処理を渡しているだけなので実装自体は、簡単に行えます。

```Python
import datetime
from my_library import mqtt # 独自ライブラリ
import numpy as np
import json
import Database_Control # 独自メソッド

class Soil():
    def __init__(self):
        self.soil_dataList = [] # 送られてきたデータを格納

        self.setTime = datetime.datetime.now() + datetime.timedelta(hours=1)
        self.db = Database_Control.DataBase_Control()

    def soil_data_DataBase_Set(self, data):

        setData = json.loads(data)

        self.soil_dataList.append(setData["soil_value"])

        if datetime.datetime.now().strftime('%H') == self.setTime.strftime('%H'):
            # データベースに追加
            self.db.newCommand('INSERT INTO soil(Date,soil_value) VALUES({},{}); '.format(
                datetime.datetime.now().strftime('\'%Y-%m-%d %H:%M:%S\''),
                np.mean(self.soil_dataList),
                ))
            self.db.data_acss()  # データベース操作

            # 配列初期化
            self.soil_dataList.clear()

            self.setTime = datetime.datetime.now() + datetime.timedelta(hours=1)


test = Soil()

mqtt.Mqtt_code_sub("kumeta/kumeta/#", "localhost",
                   syori=test.soil_data_DataBase_Set).Mqttsub()
```

###　コード解説
 setTime は、データベースに登録する時刻を表しています。
 ここでは、今の時間から 1 時間後に登録されます。

 db は、データベースに登録用ですね。これは、自分のデータベースにアクセスするための処理が主に書いてあります。
 いろいろデータベース周りのライブラリがあるので探すと面白いです。

 ```python
 def __init__(self):
       self.soil_dataList = [] # 送られてきたデータを格納

       self.setTime = datetime.datetime.now() + datetime.timedelta(hours=1)
       self.db = Database_Control.DataBase_Control()
 ```

 Mqtt が受信した際の処理です。
 引数として受信したデータを受け取ってきます。

 1. そのままだと文字列として認識されているので Json に変換します
 2. 今の時刻と予定時刻が一致した場合データベースに登録します
 3. sql コマンドをセットして実行します。ここで、時刻に関してセットしていますがこの処理は、データベース作成時に設定しておけば自動的に追記してくれるようになります。
 4. 最後に配列や時刻を再定義する

 ```python
 def soil_data_DataBase_Set(self, data):
        #   1
        setData = json.loads(data)

        self.soil_dataList.append(setData["soil_value"])
        # ２
        if datetime.datetime.now().strftime('%H') == self.setTime.strftime('%H'):
            # データベースに追加
            # ３
            self.db.newCommand('INSERT INTO soil(Date,soil_value) VALUES({},{}); '.format(
                datetime.datetime.now().strftime('\'%Y-%m-%d %H:%M:%S\''),
                np.mean(self.soil_dataList),
                ))
            self.db.data_acss()  # データベース操作

            # 配列初期化
            # ４
            self.soil_dataList.clear()

            self.setTime = datetime.datetime.now() + datetime.timedelta(hours=1)
 ```

## まとめ

今回は、初歩的なことをやりました。
AWS や GCP を扱う前に練習がてらやってみるといいと思います。
ラズパイは、学習用として非常に優れていると考えていて
プロトタイプを作成する場合、私はよく扱っています。

また、私の通っている大学では、データベースを習いますが、なぜか皆使用しません
きっとコマンドプロンプトでの作業になれない状態で学んだから苦手意識があるのかもしれません。