M5Stackで始めるセンサ・インターフェーシング (6) 紫外線センサVEML6070を利用

 UVインデックスは広く使われていて、日焼け止めなどの業界ではなくてはならない指標です。0~13の数値で表します。それを大雑把にグルーピングして、5段階で「多い」とか「少ない」と表現します。

  • Extreme  10.4~12.3
  • Very High 7.7~10.4
  • High    5.7~7.7
  • Moderate  2.2~5.7
  • Low     0~2.2

 とてラフな指標にみえます。

気象庁のUVインデックスの精密極まる算出方法

 UVインデックスを求める方法を解説したWebページはこちらです。
 0.5nmごとに紫外線の強度を測る装置で、地上に降り注ぐ紫外線を観測します。太陽から地球に届く光の中で、290nm以下の波長は地上に到達できません。測定した紫外線の強度に、お肌に影響のある波長に対して重みづけをしたフィルタを掛けます。300nmまでが1.0で、320nmでは0.0という急峻なフィルタです。そのフィルタリングしたデータを積分します。そうすると、310nm付近にピークをもった波長-強度のグラフが得られます。

 325nm以上のデータは、324nmの強度の0.0722にしかなりませんから、UVインデックスを求めるときに無視できると思われます。というより、320nm以上の波長はUVインデックスには不要な波長と定義しています。

紫外線センサVEML6070のおもなスペック

  • 電源電圧 2.7~5.5V
  • UVスペクトル感知範囲 320~410 nm(ピークは355 nm)
  • インターフェース I2C(最大400kHz)
  • スレーブ・アドレス 0x71と0x73(8ビット)

 VEML6070が実装されたブレークアウト・ボードはSeeed製で、スイッチサイエンスから入手しました。Groveのコネクタが実装され、int信号はランドが用意されています。

データ形式

 ultraviolet light dataのデータは、0x73(0x39)で上位1バイト、0x71(0x38)で下位1バイトが読み出せます。このデータは紫外線の輝度のデータで、UVインデックスではありません。VEML6070のデータシートには輝度を得る方法までしか書かれていません。アプリケーション・ノートにUVインデックスへの変換方法が書かれています。

 データシートに掲載されている波長と強度のグラフでは、355nmにピークがあり、測るべき波長の300~325nmは約15%以下の感度でしかありません。先ほどの気象庁の測定方法では、85%がUVインデックスを表すには不要なデータに相当します。
 このデバイスの特性および内蔵フィルタが、UVインデックスの測定に向いているように思えません。

輝度情報を読み出す

 ボードにはGroveコネクタが用意されているので、M5StackとGroveケーブルで接続します。I2CScannerを動かします。0x38と0x39に見つけました。

 データシートには、ACKをクリアし、0x06を0x38のアドレスに書き込むように指示されています。この二つのコマンドの実行をsetup()で行います。
 loop()では、上位バイトと下位バイトを読んで16ビットに変換します。この読み出したデータがultraviolet light dataになります。

#include <Wire.h>
#include <M5Stack.h>
#define VEML6070_address_L 0x38
#define VEML6070_address_H 0x39
#define VEML6070_ARA 0x0c
#define VEML6070_command 0x02

void setup() {
Serial.begin(9600);
Wire.begin();
Serial.println("start VEML6070");

M5.begin();
M5.Lcd.fillScreen(BLACK);
M5.Lcd.setTextSize(2);
M5.Lcd.setTextColor(YELLOW);
M5.Lcd.setCursor(0, 10);
M5.Lcd.print("start VEML6070");

Wire.beginTransmission(VEML6070_ARA);
Wire.write(0x01); // clear ACK
Wire.endTransmission();
Wire.beginTransmission(VEML6070_address_L);
Wire.write(0x06); // IT1=LOW, IT0=HIGH ==1T
Wire.endTransmission();
}

void loop() {
Wire.requestFrom(VEML6070_address_H, 1);
int data = Wire.read();
Wire.requestFrom(VEML6070_address_L, 1);
data = data << 8 | Wire.read();
Serial.println( "\n ultraviolet light data " + String(data));

M5.Lcd.fillRect(0, 30, 200, 50, BLACK);
M5.Lcd.setCursor(0, 30); M5.Lcd.print(" light data " + String(data));

delay(2000);
}

 実行している様子です。机の上ではほぼ0です。LEDライトを直接センサの上に置くと150ぐらいまで上昇します。

 室外に持ち出しました。2019/5/8の8時ごろは1300ぐらい、お昼ごろは約1500のデータになりました。

UVインデックス

 アプリケーション・ノートにそって、UVインデックスを求めます。抵抗Rextの値が必要なのですが、回路図が見つかりません。基板上の抵抗でそれらしいのが270kΩです。light dataとUVindexの関係をif文で記述しました。
 0=Low, 1=Moderate, 2=High, 3=VeryHigh, 4=Extremeとしました。

#include <Wire.h>
#include <M5Stack.h>
#define VEML6070_address_L 0x38
#define VEML6070_address_H 0x39
#define VEML6070_ARA 0x0c
#define VEML6070_command 0x02

byte UVindex = 0; // 0=Low, 1=Moderate, 2=High, 3=VeryHigh, 4=Extreme

void setup() {
Serial.begin(9600);
Wire.begin();
Serial.println("start VEML6070");

M5.begin();
M5.Lcd.fillScreen(BLACK);
M5.Lcd.setTextSize(2);
M5.Lcd.setTextColor(YELLOW);
M5.Lcd.setCursor(0, 10);
M5.Lcd.print("start VEML6070");

Wire.beginTransmission(VEML6070_ARA);
Wire.write(0x01); // clear ACK
Wire.endTransmission();
Wire.beginTransmission(VEML6070_address_L);
Wire.write(0x06); // IO1=LOW, IT0=HIGH ==1T
Wire.endTransmission();
}

void loop() {
Wire.requestFrom(VEML6070_address_H, 1);
int data = Wire.read();
Wire.requestFrom(VEML6070_address_L, 1);
data = data << 8 | Wire.read();
Serial.println( "\n ultraviolet light data " + String(data));

M5.Lcd.fillRect(0, 30, 320, 50, BLACK);
M5.Lcd.setCursor(0, 30); M5.Lcd.print("light data " + String(data));
if (data <= 560) {UVindex = 0;
}else if (data >= 561 && data <= 1120) {UVindex = 1;
}else if (data >= 1121 && data <= 1494) {UVindex = 2;
}else if (data >= 1495 && data <= 2054) {UVindex = 3;
}else if (data >= 2055 ) {UVindex = 4;
}
Serial.println( "UVindex " + String(UVindex));
M5.Lcd.print(",UVindex " + String(UVindex));

delay(2000);
}

 実行している様子です。UVindex はUVインデックスの値ではなく、5段階の指標の値です。

GUIを活用

 指標に合わせて、色の帯を作りました。

  • Extreme  10.4~12.3 紫色
  • Very High 7.7~10.4  赤色
  • High    5.7~7.7  オレンジ色
  • Moderate  2.2~5.7  黄色
  • Low      0~2.2   緑色

 UVindexの値に合わせて、▼印が指標を指し、文字を表示します。文字は一度表示すると残っています。

#include <Wire.h>
#include <M5Stack.h>
#define VEML6070_address_L 0x38
#define VEML6070_address_H 0x39
#define VEML6070_ARA 0x0c
#define VEML6070_command 0x02

byte UVindex = 0; // 0=Low, 1=Moderate, 2=High, 3=VeryHigh, 4=Extreme

void setup() {
Serial.begin(9600);
Wire.begin();
Serial.println("start VEML6070");

M5.begin();
M5.Lcd.fillScreen(BLACK);
M5.Lcd.setTextSize(2);
M5.Lcd.setTextColor(YELLOW);
M5.Lcd.setCursor(0, 10);
M5.Lcd.print("start VEML6070");

Wire.beginTransmission(VEML6070_ARA);
Wire.write(0x01); // clear ACK
Wire.endTransmission();
Wire.beginTransmission(VEML6070_address_L);
Wire.write(0x06); // IO1=LOW, IT0=HIGH ==1T
Wire.endTransmission();

M5.Lcd.fillRect(60, 70, 300, 34, TFT_PURPLE);
M5.Lcd.fillRect(60, 104, 300, 34, TFT_RED);
M5.Lcd.fillRect(60, 138, 300, 34, TFT_ORANGE);
M5.Lcd.fillRect(60, 172, 300, 34, TFT_YELLOW);
M5.Lcd.fillRect(60, 206, 300, 34, TFT_GREEN);
}

void loop() {
Wire.requestFrom(VEML6070_address_H, 1);
int data = Wire.read();
Wire.requestFrom(VEML6070_address_L, 1);
data = data << 8 | Wire.read();
Serial.println( "\n ultraviolet light data " + String(data));

M5.Lcd.fillRect(0, 30, 320, 40, BLACK);
M5.Lcd.setCursor(0, 30); M5.Lcd.print("UV light data " + String(data));
if (data <= 560) {UVindex = 0;
}else if (data >= 561 && data <= 1120) {UVindex = 1;
}else if (data >= 1121 && data <= 1494) {UVindex = 2;
}else if (data >= 1495 && data <= 2054) {UVindex = 3;
}else if (data >= 2055 ) {UVindex = 4;
}

// UVindex=2; // debug

Serial.println( "UVindex " + String(UVindex));
// M5.Lcd.print(",UVindex " + String(UVindex));
M5.Lcd.fillRect(0, 60, 50, 220, BLACK);
byte arrow = 192 - UVindex * 34;
M5.Lcd.fillTriangle(50, 30+arrow, 20, 50+arrow, 20, 10+arrow, WHITE);
M5.Lcd.setTextColor(BLACK);
if (UVindex == 0) M5.Lcd.drawCentreString("Low", 130, 13+arrow, 2);
if (UVindex == 1) M5.Lcd.drawCentreString("Moderate", 130, 13+arrow, 2);
if (UVindex == 2) M5.Lcd.drawCentreString("High", 130, 13+arrow, 2);
M5.Lcd.setTextColor(WHITE);
if (UVindex == 3) M5.Lcd.drawCentreString("Very High", 130, 13+arrow, 2);
if (UVindex == 4) M5.Lcd.drawCentreString("Extreme", 130, 13+arrow, 2);

delay(2000);
}

 実行している様子です。

前へ

M5Stackで始めるセンサ・インターフェーシング (5) 重量センサとHX711

次へ

M5Stackで始めるセンサ・インターフェーシング (7) 超音波距離センサを利用