Портативный измеритель ультрафиолетового излучения с Ардуино

Многие люди испытывают дерматологические проблемы и не могут находиться долго на открытом солнце. Данный проект поможет создать своими руками на основе Ардуино Нано и УФ-датчика измеритель ултрафиолетового излучения. Автор проекта назвал данное устройство UltraV.

Оно создано на основе Arduino Nano rev3, УФ-датчика, DC/DC-преобразователя для повышения напряжения питания выше 3В и небольшого OLED-дисплея. Главная цель заключалась в том, чтобы сделать устройство портативным для измерения УФ-индекса в любой момент и в любом месте.

Шаг 1: Детали и компоненты

Нам понадобятся достаточное количество деталей и компонентов для проекта УФ-измерителя на Ардуино:

  • Микроконтроллер Arduino Nano rev.3
  • Ультрафиолетовый датчик ML8511
  • 128×64 OLED дисплей (SSD1306)
  • Преобразователь MT3608 DC-DC
  • Аккумулятор CR2
  • Держатель батареи CR2
  • Переключатель
  • Корпус

Шаг 2: Датчик

ML8511 - это УФ-датчик, который подходит для получения УФ-интенсивности в помещении или на открытом воздухе. ML8511 оснащен внутренним усилителем, который преобразует фото-ток в напряжение в зависимости от интенсивности УФ-излучения. Эта уникальная функция предлагает простой интерфейс для внешних схем, таких как АЦП. В режиме отключения питания типичный ток в режиме ожидания составляет 0,1 мкА, что позволяет увеличить срок службы батареи.

Особенности:

  • Фотодиод чувствителен к УФ-А и УФ-В
  • Встроенный операционный усилитель
  • Аналоговый выход напряжения
  • Низкий ток питания (300 мкА) и низкий ток в режиме ожидания (0,1 мкА).
  • Малый и тонкий комплект для поверхностного монтажа (4,0 мм х 3,7 мм х 0,73 мм, 12-контактный керамический QFN)

К сожалению, не было возможности найти какой-либо УФ-прозрачный материал для защиты датчика. Любая прозрачная крышка, которую тестировали (пластик, стекло и т.д.), ослабляла УФ-измерение. Лучший выбор - стекло из плавленого кварца, но тяжело найти такое по разумной цене, поэтому было принято решение оставить датчик вне корпуса под открытым небом.

Шаг 3: Как работает датчик

Просто включите устройство и наведите его на солнце в течение нескольких секунд, удерживая его в соответствии с направлением солнечных лучей. Затем смотрите на дисплей: индекс слева всегда показывает мгновенное значение (каждые 200 мс), в то время как показания справа являются максимальным показанием, сделанным во время этого сеанса: это то, что вам нужно.

В нижней левой части дисплея сообщается также эквивалентная номенклатура Всемирной организации здравоохранения, ВОЗ, как на фото выше (LOW, MODERATE, HIGH, VERY HIGH, EXTREME) для измеренного УФ-индекса - Низкий, Умеренный, Высокий, Очень высокий, Экстремальный.

Шаг 4: Напряжение и аккумулятор

Был выбран аккумулятор CR2 ​​из-за размера и емкости (800 мАч). Устройство использовалось в течение всего лета и аккумулятор все еще 2.8В, поэтому все вполне довольны выбором. Когда устройство работает, цепь выдает около 100 мА, но измерение показаний занимает не более нескольких секунд. Поскольку номинальное напряжение батареи составляет 3 В, добвлен преобразователь постоянного тока DC-DC, чтобы довести напряжение до 9 вольт и подключить его к выходу Vin.

Чтобы иметь индикацию напряжения батареи на дисплее, использован аналоговый вход (A2). Аналоговые входы Arduino могут использоваться для измерения постоянного напряжения от 0 до 5 В, но для этого метода требуется калибровка. Для выполнения калибровки понадобится мультиметр. Сначала включите цепь с вашей последней батареей (CR2) и не используйте питание USB от компьютера; измерять 5V на Arduino от регулятора (находится на выводе Ардуина 5V): это напряжение используется для опорного напряжения АЦП Ардуино по умолчанию. Теперь поместите измеренное значение в код следующим образом (предположим, что вы прочитали 5.023):

voltage = ((long)sum / (long)NUM_SAMPLES * 5023) / 1024.0;

В эскизе взято среднее значение из 10 измерений.

Шаг 5: Схема и соединения

Принципиальная схема Ультрафиолетового измерителя Ардуино выглядит таким образом:

Шаг 6: Программное обеспечение

Для дисплея была использована U8g2lib, которая является очень гибкой и мощной для подобных OLED-дисплеев, широкий выбор шрифтов и хорошие функции позиционирования.

Что касается показания напряжения от ML8511, был использован опорный контакт 3,3 В Arduino (с точностью до 1%) в качестве основы для преобразователя АЦП. Таким образом, делая аналого-цифровое преобразование на выводе 3.3V (подключая его к A1), а затем сравнивая это показание с показанием датчика, мы можем экстраполировать истинное значение, независимо от того, что на VIN (пока оно выше 3.4В).

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

Скачайте или скопируйте код ниже:

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

Шаг 7: Корпус и сборка

После нескольких (плохих) тестов при ручной резки прямоугольного окна дисплея в купленной пластиковой коробке было принято решение создать собственный корпус. Таким образом, с помощью приложения САПР была разработана коробка, а чтобы она была как можно меньше, батарею CR2 смонтировали снаружи на задней стороне (с держателем батареи, наклеенным на корпус).

Вы можете скачать файл STL для корпуса ниже:

Шаг 8: Итоговый результат

На этом наш измеритель ультрафиолетового излучения на Ардуино готов.

Ардуино+