亲,“电路城”已合并升级到更全、更大、更强的「新与非网」。点击查看「新与非网」

本网页已闲置超过3分钟,按键盘任意键或点击空白处,即可回到网页

便携式紫外线指数计—UltraV

发布时间:2022-10-31
分享到:

便携式紫外线指数计—UltraV

发布时间:2022-10-31
分享到:

一种便携式小型装置,可在任何地方检测太阳光中的紫外线指数。

最近要准备去海边度假,我不能让自己暴露在阳光下,于是我用我本该在海滩上度过的时间,做了一个紫外线测量仪。

它构建在Arduino Nano rev3上,带有紫外线传感器,一个DC/DC转换器,以提高3v电池电压,以及一个小型OLED显示屏。我的主要目标是保持它的便携性,这样我就可以很容易地知道任何时间任何地点的紫外线指数。

第 1 步:零部件

  • 微控制器Arduino Nano rev.3
  • ML8511紫外线传感器
  • 128×64 OLED显示器(SSD1306)
  • MT3608直流-直流升压
  • CR2电池
  • CR2电池座
  • 开关

第 2 步:传感器

ML8511 (Lapis Semiconductors)是一种紫外线传感器,适用于获取室内或室外的紫外线强度。ML8511内置放大器,可根据紫外线强度将光电流转换为电压。这一独特的特性提供了与ADC等外部电路的简单接口。在关机模式下,待机电流通常为0.1µA,从而使电池寿命更长。

特点:

  • 光电二极管对UV-A和UV-B敏感
  • 嵌入式运算放大器
  • 模拟电压输出
  • 低供电电流(300 μ A型)和低待机电流(0.1 μ A型)
  • 小而薄的表面安装包(4.0mm x 3.7mm x 0.73mm, 12针陶瓷QFN)

不过不巧的是,我没有机会找到任何紫外线透明的材料来保护传感器。我测试的任何一种透明覆盖物(塑料、玻璃等)都在衰减紫外线测量值。更好的选择似乎是石英熔融硅玻璃,但我没有找到任何一个合理的价格,所以我决定把传感器放在盒子外面,在户外。

第 3 步:操作

测量时,只需打开设备,将其对准太阳几秒钟,保持与太阳光的方向一致。然后观察显示器:左边的索引总是显示即时测量值(每200毫秒一个),而右边的读数是在此会话中获得的最大读数:这是您需要的。

在显示屏的左下方,还报告了世界卫生组织对所测紫外线指数的等效命名法(LOW、MODERATE、HIGH、VERY HIGH、EXTREME)。

第 4 步:电池电压和读数
我选择了CR2电池,因为它的大小和容量(800毫安时)。整个夏天我都在用紫外线,电池的读数仍然是2.8 v,所以我对这个选择非常满意。当运行时,电路损耗约100毫安,但读数测量不需要超过几秒钟。由于电池标称电压为3v,我添加了一个DC-DC升压转换器,将电压提高到9伏,并将其连接到Vin引脚。

为了在显示器上有电池电压指示,我使用了模拟输入(A2)。Arduino模拟输入可以用来测量0到5V之间的直流电压,但这种技术需要校准。为了进行校准,你需要一个万用表。首先用最后一块电池(CR2)为电路供电,不要使用电脑的USB电源;从调节器测量Arduino上的5V(在Arduino 5V引脚上找到):默认情况下,该电压用于Arduino ADC参考电压。现在将测量值放入如下示意图中(假设我读取5.023):

电压= ((long)sum / (long)NUM_SAMPLES * 5023) / 1024.0;

在草图中,我将电压测量作为超过10个样本的平均值。

第 5 步:原理图和连接

第 6 步:软件
对于显示器,我使用了U8g2lib,它非常灵活和强大,对于这种OLED显示器,允许广泛的字体选择和良好的定位功能。

关于从ML8511读取的电压,我使用3.3v Arduino参考引脚(精度在1%以内)作为ADC转换器的基数。因此,通过在3.3V引脚上进行模拟到数字的转换(通过将其连接到A1),然后将该读数与传感器的读数进行比较,我们可以推断出真实的读数,无论VIN是什么(只要它高于3.4V)。

int uvLevel = averageAnalogRead(UVOUT);
int refLevel = averageAnalogRead(REF_3V3);
float outputVoltage = 3.3 / refLevel * uvLevel;

第 7 步:机箱外壳

在手工切割商业塑料盒子上的矩形展示窗的几次(糟糕的)试验后,我决定为它设计自己的。因此,我用CAD应用程序设计了一个盒子,为了使它尽可能小,我将CR2电池安装在背面的外部(用电池支架粘在盒子本身上)。

第 8 步:未来可能的改进

  • 利用紫外光谱仪在各种条件下测量实际的实时紫外指数值(紫外光谱仪非常昂贵);
  • 同时用Arduino单片机记录ML8511的输出;
  • 编写算法,在广泛的大气条件下,将ML8511输出与实际UVI值实时关联。

第 9 步:图像库

UltraV:

    /* 
 Fabio Marzocca @ 2018
 Analog to digital conversions rely completely on VCC. We assume
 this is 5V but if the board is powered from USB this may be as high as 5.25V or as low as 4.75V:
 http://en.wikipedia.org/wiki/USB#Power Because of this unknown window it makes the ADC fairly inaccurate
 in most cases. To fix this, we use the very accurate onboard 3.3V reference (accurate within 1%). So by doing an
 ADC on the 3.3V pin (A1) and then comparing this against the reading from the sensor we can extrapolate
 a true-to-life reading no matter what VIN is (as long as it's above 3.4V).

v. 2.0.0 - July 2018
   - moved from 16x2 LCD to OLED
v. 2.0.1 - Sept 2018
   - changed read battery function
*/

#include <Arduino.h>
#include <U8g2lib.h>
#include <Wire.h>

U8G2_SSD1306_128X64_NONAME_F_SW_I2C u8g2(U8G2_R0, /* clock=*/ SCL, /* data=*/ SDA, /* reset=*/ U8X8_PIN_NONE);   // All Boards without Reset of the Display

#define FIRST_ROW_Y 16
#define FIRST_ROW_X 16
#define BOX_H 38

//Hardware pin definitions
const int UVOUT = A0; //Output from the sensor
const int REF_3V3 = A1; //3.3V power on the Arduino board
const int VBATT = A2; //Battery voltage

float maxUV = 0; //Max UV index read


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

    //Use the 3.3V power pin as a reference to get a very accurate output value from sensor
    float outputVoltage = 3.3 / refLevel * uvLevel;

    float uvIntensity = mapfloat(outputVoltage, 0.99, 2.6, 0.0, 15.0); //Convert the voltage to a UV intensity level

    readBattery();
    if (maxUV < uvIntensity) {
        maxUV = uvIntensity;
      }
    u8g2.drawFrame(0,FIRST_ROW_Y+1,128,BOX_H);
    u8g2.setFont(u8g2_font_logisoso18_tf);

    //Instant UV
    u8g2.setCursor(10,40);
    u8g2.print(uvIntensity);
    //UV Max
    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);
}

// Reads maxUV and prints the UV category
void showUVCategory() {
  char strCat[12];

  byte categ = (byte)(maxUV+0.5);  //round up
  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);
  
}

//Takes an average of readings on a given pin
//Returns the average
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);  
}

//The Arduino Map function but for floats
//From: http://forum.arduino.cc/index.php?topic=3922.0
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; //Battery minimum voltage read on Vcc
   byte batteryBar; //Battery progress bar
   int barStep = 140; //step for battery progress bar (235)

   batteryBar = (battery - VccMin)/barStep;
   
       //battery icon
    u8g2.setFont(u8g2_font_open_iconic_embedded_2x_t);
    u8g2.drawGlyph(1, FIRST_ROW_Y, 73 );
    
    // battery status cursor
    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);
    }
    
    // battery voltage
    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
    // take a number of analog samples and add them up
    while (sample_count < NUM_SAMPLES) {
        sum += analogRead(VBATT);
        sample_count++;
        delay(2);
    }
        // calculate the voltage
    // use 5000 for a 5.0V ADC reference voltage
    // 5020V is the calibrated reference voltage (in millivolts) for my project
    voltage = ((long)sum / (long)NUM_SAMPLES * 5020) / 1024.0;
    return voltage; //Vbattery in millivolts
}
    

如果您对此项目有任何想法、意见或问题,请在下方留言。

以上内容翻译自网络,原作者:fmarzocca,如涉及侵权,可联系删除。

加入微信技术交流群

技术交流,职业进阶

关注与非网服务号

获取电子工程师福利

加入电路城 QQ 交流群

与技术大牛交朋友

讨论