NefryBTでGroveアルコールセンサを使う(正確なアルコール濃度は測定できないが使えた)

# Groveアルコールセンサを使おうと思った
Groveのアルコールセンサを秋葉原で買ったので、[Nefry BT R3](https://dotstud.io/docs/nefrybt)で使おうと思いました。
SeeedのWiki([Grove - Alcohol Sensor](http://wiki.seeedstudio.com/Grove-Alcohol_Sensor/))を見ながら使い方を確認します。
そして、以下の通り手順を踏んで行ったのですが・・・

1. センサをGroveソケットの A0 端子に接続する
1. [Grove - Alcohol Sensor](http://wiki.seeedstudio.com/Grove-Alcohol_Sensor/)のコードをNefry用に書き換える
1. Arduino IDE にてコンパイル＆書き込み
1. Arduino IDE のシリアルモニタにてログを確認する

**思い通りの値が出ない。**  

具体的に言えば、このセンサは空気中のアルコール濃度(ppm)が測れるものなのですが、ppmが上手く導き出せないということです。
しかし、アルコールには反応しているっぽいので、**「アルコールに反応するスイッチ」**としては使えそうという話をまとめた記事になります。
とにかく使いたい人は[ここから読んで](#無理やりそれっぽく使ってみた)ください。

# 空気中のアルコール濃度は出せなそう
以下のようにサンプルのコードを書き換えたのですが上手くいきません。
(書き換えたコードは[こちらのリポジトリ](https://github.com/TakedaHiromasa/Nefry_AlcoholSensor)にあります)

```C
#include <Nefry.h>
#define heaterSelPin A1

void setup() {
    // Serial.begin(9600); //デフォルトで115200bps
    pinMode(heaterSelPin,OUTPUT);
    digitalWrite(heaterSelPin,LOW);
    delay(2000);
}

void loop() {
    float sensor_volt;
    float RS_air; //  Get the value of RS via in a clear air
    float sensorValue;

/*--- Get a average data by testing 100 times ---*/
    for(int x = 0 ; x < 100 ; x++)
    {
        sensorValue = sensorValue + analogRead(A0);
    }
    sensorValue = sensorValue/100.0;
/*-----------------------------------------------*/

    sensor_volt = sensorValue/4095*5.0;
    RS_air = sensor_volt/(5.0-sensor_volt); // omit *R16
    Nefry.print("sensor_volt = ");
    Nefry.print(sensor_volt);
    Nefry.println("V");
    Nefry.print("RS_air = ");
    Nefry.println(RS_air);
    Nefry.ndelay(1000);
}
```

```C
#include <Nefry.h>
#define heaterSelPin A1

void setup() {
  pinMode(heaterSelPin, OUTPUT);  // set the heaterSelPin as digital output.
  digitalWrite(heaterSelPin, LOW); // Start to heat the sensor
  Nefry.ndelay(2000);
}

void loop() {
  float sensor_volt;
  float RS_gas; // Get value of RS in a GAS
  float RS_air = -100.0;  //ここに最初のテストコードで得られた値を入れる
  float ratio; // Get ratio RS_GAS/RS_air
  int sensorValue = analogRead(A0);
  sensor_volt = (float)sensorValue / 4095.0 * 5.0;
  RS_gas = sensor_volt / 5.0 - sensor_volt; // omit *R16

  /*-Replace the name "R0" with the value of R0 in the demo of First Test -*/
  ratio = RS_gas / RS_air; // ratio = RS/R0
  /*-----------------------------------------------------------------------*/

  Nefry.print("sensor_volt = ");
  Nefry.println(sensor_volt);
  Nefry.print("RS_ratio = ");
  Nefry.println(RS_gas);
  Nefry.print("Rs/R0 = ");
  Nefry.println(ratio);

  Nefry.print("\n");
  Nefry.ndelay(1000);
}
```

## Arduinoではそれっぽく動く
まず前提として、Arduinoでは上手くいっていて、公式の提示しているデータシートに大体近い値の変化を出します。
なので、センサの不良ではない・・・多分。
<img src="https://qiita-image-store.s3.amazonaws.com/0/259617/905fab34-d288-e893-3309-617f899efd6b.jpeg" width="500">

## やったこと
* GroveソケットでなくGPIOに配線してみる
* 使うピンを変える
* センサの`SCL`を最初から`GND`につなぐ
* ArduinoのADC(ADコンバータ)は`分解能1024`ですが、Nefry(ESP32)のADCは`分解能4095`なので書き換える。

```C
sensor_volt = sensorValue/4095*5.0;
```

* 本来このセンサーは`5v`動作で、Nefry Groveソケットは`3.3v`出力なので、例えば以下のように全体を書き換える。

```C
sensor_volt = sensorValue/4095*3.3;
```

* センサーのVCCをちゃんとGPIOの5vに繋いでみる
* リファレンス通り48時間予熱してみる
* 以下のような **「アナログpinをデジタル出力として使った後に、他のアナログ入力を読み取ろうとする場合は間に時間を開けたほうがいいよ」** というような公式の記述を見て、`setup`の最後に`delay(2000);`を入れてみる

> The ATmega datasheet also cautions against switching analog pins in close temporal proximity to making A/D readings (analogRead) on other analog pins. This can cause electrical noise and introduce jitter in the analog system. It may be desirable, after manipulating analog pins (in digital mode), to add a short delay before using analogRead() to read other analog pins.
> [*- Analog Input Pins*](https://www.arduino.cc/en/Tutorial/AnalogInputPins)

これらの試みをいろんな組み合わせで、公式で提示されているテストコード2つに適用してみましたが、公式で提示されているセンサ値とガス濃度の相関関係に合致してくれません・・・・
それどころか、日によって**値が大きく違ったり** 、**コンパイルが通ったり、通らなかったり** する有様です・・・

<img src="https://qiita-image-store.s3.amazonaws.com/0/259617/2bfa91bf-355e-cf53-1d59-93618106b991.png" width="400">

 
本当は **「ちゃんと使えませんでした」** という記事は書きたくなかったんですが、とにかくこれだけに文字通り3日3晩悩み、無駄な数日を過ごしたので正確な`ppm`を出すことは **諦めました。**

何か解法や、情報がある方はコメントしていただけると嬉しいです。

# 無理やりそれっぽく使ってみた
このままではこの記事の価値がないので、無理やりそれっぽく使ってみました。
センサにアルコールを近づけるとちゃんと値が変化するので、 **アルコールは検知している** ということで、アルコールに反応するスイッチとして使います。

## 概要
常に空気中(部屋)のアルコール濃度を計測。
Nefryのボタンを押して、センサーに3秒息を吹きかけると、呼気中にアルコールが含まれているか含まれていないかを判定してくれます。
<img src="https://qiita-image-store.s3.amazonaws.com/0/259617/b403b2c4-be70-8c71-400e-dac1885c57c6.gif" width="400">

## ソースコード
ここに載せているコードは、[こちらのリポジトリ](https://github.com/TakedaHiromasa/Nefry_AlcoholSensor)にあげてます。
シリアルモニタで動作を確認する最小限のコードは以下の通り。

```C:nefly_alcohol_checker.ino
#include <Nefry.h>

#define heaterSelPin A1

void setup() {
  Nefry.enableSW();

  pinMode(heaterSelPin, OUTPUT);  // set the heaterSelPin as digital output.
  digitalWrite(heaterSelPin, LOW); // Start to heat the sensor
  Nefry.ndelay(2000);
}

void loop() {
  float airValue = 0;

  /*--- Get a average data by testing 100 times ---*/
  for (int x = 0 ; x < 100 ; x++) {
    airValue = airValue + analogRead(A0);
  }
  airValue = 4095.0 - (airValue / 100.0);
  /*-----------------------------------------------*/
  
  //スイッチが押されたらアルコールチェック
  if (Nefry.readSW()) {
    alcohol_check(airValue, 200.0);
  }

  Nefry.print("sensor_value = ");
  Nefry.println(airValue);

  Nefry.ndelay(1000);
}

void alcohol_check(float airValue, float threshold) {
  float breathValue = 0;

  Nefry.println("\n-息を吐いて!-");
  for (int x = 1 ; x <= 6 ; x++) { // 3秒間繰り返す
    if (x % 2) {
      int cnt = x / 2 + 1;
      Nefry.print(String(cnt) + " ");
    }
    breathValue = breathValue + analogRead(A0);
    Nefry.ndelay(500);
  }
  breathValue = 4095 - breathValue / 6.0;
  Nefry.println("\nair = " +  String(airValue));
  Nefry.println("breath = " +  String(breathValue));

  if (threshold < (breathValue - airValue)) {
    Nefry.println("【OUT!!】");
  } else {
    Nefry.println("【SAFE】");
  }
  
  Nefry.println("------------");
  Nefry.ndelay(5000);
}

```

Nefry専用ディスプレイ持っている方は以下のコードを書き込むとディスプレイに状態を表示してくれるので、シリアルモニタで見なくても使えます。

```C:nefly_alcohol_checker_Display.ino
#include <Nefry.h>
#include <NefryDisplay.h>
#include <NefrySetting.h>

#define heaterSelPin A1

void setting() {
  Nefry.disableDisplayStatus();
}
NefrySetting nefrySetting(setting);

void setup() {
  Nefry.enableSW();

  pinMode(heaterSelPin, OUTPUT);  // set the heaterSelPin as digital output.
  digitalWrite(heaterSelPin, LOW); // Start to heat the sensor
  Nefry.ndelay(2000);
}

void loop() {
  float airValue = 0;

  /*--- Get a average data by testing 100 times ---*/
  for (int x = 0 ; x < 100 ; x++) {
    airValue = airValue + analogRead(A0);
  }
  airValue = 4095.0 - (airValue / 100.0);
  /*-----------------------------------------------*/
  
  //スイッチが押されたらアルコールチェック
  if (Nefry.readSW()) {
    alcohol_check(airValue, 200.0);
  }

  NefryDisplay.clear();
  NefryDisplay.setFont(ArialMT_Plain_24);
  NefryDisplay.drawString(0, 10, "In the air");
  NefryDisplay.setFont(ArialMT_Plain_16);
  NefryDisplay.drawString(0, 45, String(airValue));
  NefryDisplay.display();

  Nefry.print("sensor_value = ");
  Nefry.println(airValue);

  Nefry.ndelay(1000);
}

void alcohol_check(float airValue, float threshold) {
  float breathValue = 0;

  NefryDisplay.clear();
  NefryDisplay.setFont(ArialMT_Plain_24);
  NefryDisplay.drawString(0, 0, "Ready...");
  NefryDisplay.display();
  Nefry.ndelay(1000);
  NefryDisplay.clear();
  NefryDisplay.drawString(0, 0, "Breathe");
  NefryDisplay.drawString(60, 24, "Out!");
  NefryDisplay.display();
  NefryDisplay.setFont(ArialMT_Plain_16);

  Nefry.println("\n-息を吐いて!-");
  for (int x = 1 ; x <= 6 ; x++) { // 3秒間繰り返す
    if (x % 2) {
      int cnt = x / 2 + 1;
      Nefry.print(String(cnt) + " ");
      NefryDisplay.drawString(30 * cnt, 45,  String(cnt));
      NefryDisplay.display();
    }
    breathValue = breathValue + analogRead(A0);
    Nefry.ndelay(500);
  }
  breathValue = 4095 - breathValue / 6.0;
  Nefry.println("\nair = " +  String(airValue));
  Nefry.println("breath = " +  String(breathValue));

  NefryDisplay.clear();
  NefryDisplay.setFont(ArialMT_Plain_24);
  if (threshold < (breathValue - airValue)) {
    NefryDisplay.drawString(0, 10, "OUT!!");
    Nefry.println("【OUT!!】");
  } else {
    NefryDisplay.drawString(0, 10, "SAFE");
    Nefry.println("【SAFE】");
  }
  NefryDisplay.setFont(ArialMT_Plain_16);
  NefryDisplay.drawString(0, 45,  String(airValue));
  NefryDisplay.drawString(60, 45,  "/");
  NefryDisplay.drawString(70, 45,  String(breathValue));
  NefryDisplay.display();
  Nefry.println("------------");
  Nefry.ndelay(5000);
}
```

私の環境では、全くアルコールを飲んでいない状態でセンサーに息を吹きかけると、大体`+200`までの範囲で値が変化するので、それ以上の値が出たら呼気中にアルコールが含まれているとして`OUT!!`と判定しています。

## 動作確認
アルコールを飲んでない状態で測定してみる。
<img src="https://qiita-image-store.s3.amazonaws.com/0/259617/b403b2c4-be70-8c71-400e-dac1885c57c6.gif" width="400">
**SAFE。**

アルコールを飲んで・・・(未成年は飲んじゃダメですよ！)
<img src="https://qiita-image-store.s3.amazonaws.com/0/259617/48275b63-e332-d570-3e68-810074a5cc54.jpeg" width="400">
測定してみる。
<img src="https://qiita-image-store.s3.amazonaws.com/0/259617/4e317c82-4224-931a-eb17-9a24e9d3792e.gif" width="400">
**OUT!!**

以上、濃度測定は難しそうですが、~~情報が少ないので~~面白いセンサーなので皆さんも使ってみましょう！！