UltraV:安全な日光浴のための携帯型UVインデックスメーター
必要な部品と用品
| 部品 | 説明 | 数量 |
|---|---|---|
| Arduino Nano R3 | 1 | |
| Texas Instruments ML-8511 | UVセンサー | 1 |
| 128x64 OLEDディスプレイ | SSD1306 | 1 |
| MT3608 DC-DCステップアップコンバーター | 3Vを昇圧 | 1 |
| CR2電池 (3V) | 1 | |
| CR2電池ホルダー | 1 |
プロジェクト概要
皮膚科の問題で日光を浴びられないため、ビーチで過ごす代わりに紫外線測定器「UltraV」を製作しました。
Arduino Nano rev.3、UVセンサー、DC/DCコンバーター(3V電池電圧を昇圧)、小型OLEDディスプレイで構成。主な目的は携帯性で、いつでもどこでもUVインデックスを即座に確認できるようにすることです。
ステップ1:パーツとコンポーネント
- マイクロコントローラー:Arduino Nano rev.3
- ML8511 UVセンサー
- 128×64 OLEDディスプレイ (SSD1306)
- MT3608 DC-DCステップアップコンバーター
- CR2電池
- CR2電池ホルダー
- スイッチ
- ケース
ステップ2:センサーについて
ML8511(ラピスセミコンダクタ製)は屋内外でUV強度を測定できるセンサーです。内蔵アンプにより光電流をUV強度に応じた電圧に変換するため、ADC等の外部回路とのインターフェースが容易です。パワーダウンモード時の待機電流は典型値0.1µAと極めて小さく、長時間の電池駆動が可能です。
特徴:
- UV-AおよびUV-Bに感度を持つフォトダイオード
- 内蔵オペアンプ
- アナログ電圧出力
- 低消費電流(典型300µA)、低待機電流(典型0.1µA)
- 小型薄型パッケージ(4.0×3.7×0.73mm、12ピン セラミックQFN)
残念ながら、UVを透過する保護材(石英ガラス等)を安価に入手できませんでした。プラスチックやガラス等の透明カバーはすべてUV測定値を減衰させるため、センサーはケース外部に露出させることにしました。
ステップ3:操作方法
測定するには、デバイスの電源を入れ、太陽の方向に向けて数秒間保持します。ディスプレイ左側のインデックスは瞬時値(200msごとに更新)、右側の値はセッション中の最大値です。この最大値が重要な指標となります。
左下にはWHO基準によるUVインデックス区分(LOW、MODERATE、HIGH、VERY HIGH、EXTREME)も表示されます。
ステップ4:電池電圧と測定
CR2電池(800mAh)を選択。サイズと容量のバランスが良く、夏の間使用しても電圧は2.8Vを維持しており満足しています。動作時の消費電流は約100mAですが、測定は数秒で完了します。公称電圧3VをMT3608で9Vに昇圧し、Vinピンに接続しています。
電池電圧をディスプレイに表示するため、アナログ入力(A2)を使用しています。Arduinoのアナログ入力は0-5VのDC電圧を測定可能ですが、校正が必要です。校正手順:
- 最終的な電池(CR2)で回路に電源を供給(PCのUSB電源は使用しない)
- Arduinoの5Vピン(レギュレータ出力)の電圧をテスターで測定。これがADCの基準電圧になります
- 測定値をスケッチに反映(例:5.023Vなら以下のように設定)
voltage = ((long)sum / (long)NUM_SAMPLES * 5023) / 1024.0;
スケッチでは10サンプルの平均を取っています。
ステップ5:回路図と接続
ステップ6:ソフトウェア
ディスプレイにはU8g2libを使用。フォント選択や位置制御が柔軟で、この種のOLEDに最適です。
ML8511からの電圧読み取りには、Arduinoの3.3V基準ピン(精度1%以内)をADCの基準として使用。3.3Vピン(A1に接続)とセンサー出力(A0)の両方をADC変換し、比較することで、VIN電圧に依存しない正確な測定値を得ています(VINが3.4V以上なら)。
int uvLevel = averageAnalogRead(UVOUT);
int refLevel = averageAnalogRead(REF_3V3);
float outputVoltage = 3.3 / refLevel * uvLevel;
完全なコードはこちらからダウンロードできます。
ステップ7:ケース設計
市販のプラスチックケースにディスプレイ窓を手作業で開ける試行を繰り返した後、CADで専用ケースを設計しました。コンパクトにするため、CR2電池を背面に外付けしました(電池ホルダーをケースに接着)。
STLファイルはこちらからダウンロードできます。
ステップ8:今後の改善案
- UV分光器を用いて実際のUVインデックス値を測定し、ML8511の出力と相関付け(分光器は高価)
- ML8511の出力とArduinoの測定値を同時記録
- 大気条件の幅広い範囲で、ML8511出力から実UVインデックスをリアルタイムに算出するアルゴリズムの開発
ステップ9:画像ギャラリー
ステップ10:クレジット
- Carlos Orts: プロジェクトハブ
- Arduinoフォーラム: トピック
- Starting Electronics: 記事
- U8g2lib: リファレンス
- WHO UVインデックス: ガイドライン
コード
UltraV (C/C++)
/* Fabio Marzocca @ 2018
ADC変換は完全にVCCに依存する。USB給電時は5.25V〜4.75Vと変動するため、
正確性に欠ける。オンボードの3.3V基準(精度1%以内)を使用し、
3.3Vピン(A1)とセンサー出力のADC値を比較することで、
VINに依存しない正確な測定を実現(3.4V以上なら)。
v2.0.0 - 2018年7月 - 16x2 LCDからOLEDへ移行
v2.0.1 - 2018年9月 - 電池読み取り関数を変更 */
#include <Arduino.h>
#include <U8g2lib.h>
#include <Wire.h>
U8G2_SSD1306_128X64_NONAME_F_SW_I2C u8g2(U8G2_R0, SCL, SDA, U8X8_PIN_NONE);
#define FIRST_ROW_Y 16
#define FIRST_ROW_X 16
#define BOX_H 38
const int UVOUT = A0;
const int REF_3V3 = A1;
const int VBATT = A2;
float maxUV = 0;
void setup() {
pinMode(UVOUT, INPUT);
pinMode(REF_3V3, INPUT);
pinMode(VBATT, INPUT);
u8g2.begin();
}
void loop() {
u8g2.firstPage();
do {
int uvLevel = averageAnalogRead(UVOUT);
int refLevel = averageAnalogRead(REF_3V3);
float outputVoltage = 3.3 / refLevel * uvLevel;
float uvIntensity = mapfloat(outputVoltage, 0.99, 2.6, 0.0, 15.0);
readBattery();
if (maxUV < uvIntensity) maxUV = uvIntensity;
u8g2.drawFrame(0, FIRST_ROW_Y+1, 128, BOX_H);
u8g2.setFont(u8g2_font_logisoso18_tf);
u8g2.setCursor(10, 40);
u8g2.print(uvIntensity);
u8g2.setCursor(70, 40);
u8g2.print(maxUV);
u8g2.setFont(u8g2_font_u8glib_4_tf);
u8g2.setCursor(10, 52);
u8g2.print(F("INSTANT"));
u8g2.setCursor(75, 52);
u8g2.print(F("MAXIMUM"));
showUVCategory();
u8g2.setCursor(88, 64);
u8g2.print(F("F.Marzocca"));
} while (u8g2.nextPage());
delay(200);
}
void showUVCategory() {
char strCat[12];
byte categ = (byte)(maxUV + 0.5);
if (categ >= 0 && categ < 3) strcpy(strCat, "LOW");
else if (categ >= 3 && categ < 6) strcpy(strCat, "MODERATE");
else if (categ >= 6 && categ < 8) strcpy(strCat, "HIGH !");
else if (categ >= 8 && categ < 10) strcpy(strCat, "VERY HIGH!");
else if (categ >= 11) strcpy(strCat, "EXTREME!");
u8g2.setCursor(0, 64);
u8g2.print(strCat);
}
int averageAnalogRead(int pinToRead) {
byte numberOfReadings = 16;
unsigned int runningValue = 0;
for (int x = 0; x < numberOfReadings; x++)
runningValue += analogRead(pinToRead);
runningValue /= numberOfReadings;
return runningValue;
}
float mapfloat(float x, float in_min, float in_max, float out_min, float out_max) {
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
void readBattery() {
long battery = readBattVcc();
long VccMin = 2300;
byte batteryBar;
int barStep = 140;
batteryBar = (battery - VccMin) / barStep;
u8g2.setFont(u8g2_font_open_iconic_embedded_2x_t);
u8g2.drawGlyph(1, FIRST_ROW_Y, 73);
u8g2.setFont(u8g2_font_open_iconic_play_1x_t);
for (byte i = 1; i <= batteryBar; i++) {
u8g2.drawGlyph(128 - 9 * i, FIRST_ROW_Y - 4, 75);
}
u8g2.setFont(u8g2_font_freedoomr10_tu);
u8g2.setCursor(25, FIRST_ROW_Y);
u8g2.print(float(battery) / 1000, 3);
}
long readBattVcc() {
int sum = 0;
int sample_count = 0;
long voltage = 0;
#define NUM_SAMPLES 10
while (sample_count < NUM_SAMPLES) {
sum += analogRead(VBATT);
sample_count++;
delay(2);
}
voltage = ((long)sum / (long)NUM_SAMPLES * 5020) / 1024.0;
return voltage;
}
製造プロセス
- ポータブルスポット溶接機の原理・操作・メリット:薄板金属接合の現場標準機
- 固定型と携帯型CMM測定機の選び方:知っておくべきポイント
- 自動車バッテリーの役割と仕組み:種類・構造・メンテナンス完全ガイド
- オルタネーター診断完全ガイド:電圧計・マルチメーター・ドライバー・バッテリー切離し4つのテスト方法
- オルタネーター故障の7つの明確なサイン|見逃せない警告症状
- 分圧回路ラボ:設計・測定・キルヒホフの電圧法則検証
- ジャガイモ電池の作り方:DIY電気化学電源のステップバイステップガイド
- メーター設計の理解:古典的ガルバノメーターから現代のデジタル表示まで
- マルチメーター:アナログからデジタルまで包括ガイド
- 電池構造の基礎:セル、内部抵抗、接続性
- 電池容量の理解:アンペアアワー定格と状態テスト