Edited at

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


Groveアルコールセンサを使おうと思った

Groveのアルコールセンサを秋葉原で買ったので、Nefry BT R3で使おうと思いました。

SeeedのWiki(Grove - Alcohol Sensor)を見ながら使い方を確認します。

そして、以下の通り手順を踏んで行ったのですが・・・


  1. センサをGroveソケットの A0 端子に接続する


  2. Grove - Alcohol SensorのコードをNefry用に書き換える

  3. Arduino IDE にてコンパイル&書き込み

  4. Arduino IDE のシリアルモニタにてログを確認する

思い通りの値が出ない。

具体的に言えば、このセンサは空気中のアルコール濃度(ppm)が測れるものなのですが、ppmが上手く導き出せないということです。

しかし、アルコールには反応しているっぽいので、「アルコールに反応するスイッチ」としては使えそうという話をまとめた記事になります。

とにかく使いたい人はここから読んでください。


空気中のアルコール濃度は出せなそう

以下のようにサンプルのコードを書き換えたのですが上手くいきません。

(書き換えたコードはこちらのリポジトリにあります)

#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);
}

#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では上手くいっていて、公式の提示しているデータシートに大体近い値の変化を出します。

なので、センサの不良ではない・・・多分。


やったこと


  • GroveソケットでなくGPIOに配線してみる

  • 使うピンを変える

  • センサのSCLを最初からGNDにつなぐ

  • ArduinoのADC(ADコンバータ)は分解能1024ですが、Nefry(ESP32)のADCは分解能4095なので書き換える。

sensor_volt = sensorValue/4095*5.0;


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

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


これらの試みをいろんな組み合わせで、公式で提示されているテストコード2つに適用してみましたが、公式で提示されているセンサ値とガス濃度の相関関係に合致してくれません・・・・

それどころか、日によって値が大きく違ったりコンパイルが通ったり、通らなかったり する有様です・・・

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

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


無理やりそれっぽく使ってみた

このままではこの記事の価値がないので、無理やりそれっぽく使ってみました。

センサにアルコールを近づけるとちゃんと値が変化するので、 アルコールは検知している ということで、アルコールに反応するスイッチとして使います。


概要

常に空気中(部屋)のアルコール濃度を計測。

Nefryのボタンを押して、センサーに3秒息を吹きかけると、呼気中にアルコールが含まれているか含まれていないかを判定してくれます。


ソースコード

ここに載せているコードは、こちらのリポジトリにあげてます。

シリアルモニタで動作を確認する最小限のコードは以下の通り。


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


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!!と判定しています。


動作確認

アルコールを飲んでない状態で測定してみる。



SAFE。

アルコールを飲んで・・・(未成年は飲んじゃダメですよ!)



測定してみる。



OUT!!

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