VEML6070 and VEML6075 UV sensors



  • Hi,

    I am building a UV reporting node.
    I need to present the following:

    • μW per cm²
    • UV Index (0-11+)

    What's the best pay to present it to GW and controller (Domoticz)?

    • μW per cm² -> S_UV / V_UV
      -UV Index -> S_LIGHT_LEVEL/V_LIGHT_LEVEL???

    Thanks
    Alex


  • Hardware Contributor

    for the moment, if i'm not wrong, i'm using S_UV/V_UV for UV index. and temporarily S_POWER/V_WATT for uW/cm².
    Also note, depending on usecase, VEML6075 is better regarding its voltage range (i'm using both)



  • @scalz So you are using a temp one as well for uW/cm2...

    VEML6075 is a more complex one. I think it can differentiate UV-a and UV-b or something. I ordered it a short while ago, but it would take some time before it arrives. Right now I have to think where to place it so that (1) the battery sensor is water tight if placed externally (2) direct UV exposure to the sensor (3) a transparent lid? Possible to 3d print? (4) externally or internally?


  • Hardware Contributor

    @alexsh1 for type of msg, yep..
    hehe i have some custom boards with this one :yum: I'm using it because of voltage range, one less part, and "new". UVA/UVB is a nice bonus then.
    For the enclosure, i'm still thinking the best options regarding environments i want to use them.
    If printing for outdoor, i would recommend PETG, ABS (more tricky), or better but more expensive ASA.



  • @scalz The sensor has got a certain angle (55 degrees half sensitivity if I remember correctly) to provide the correct reading so the ideal place for it would be a roof? This would put an extra hurdle on enclosure design.

    I suppose it has to be outside. I can seal atmega328p with electronic leaving only the battery box and the sensor. However, as I said before designing the box is very tricky.




  • Admin

    Normal transparent glass can apparently block up to 75% of UV-B..

    So one should look into what to use as a window over the chip area..



  • @tbowmo correct. This is why it has to be placed outside. And the sensor must be exposed as well. We need to build MySensors watertight IP67 enclosure now :-)


  • Admin

    @alexsh1

    But you can not expose the sensor to rain etc. So you still need to have some cover above the chip, and this should be UV transparent..



  • @tbowmo As I have just discovered there is a UV transparent plastic.
    Maybe the enclosure can be 3D printed with such plastic?

    There is also a UV transparent glass.



  • The sensor has a peak
    sensitivity at 355 nm.

    UV Spectrum:
    UV-a -> 315-400nm
    UV-b -> 280-315nm
    UV-c -> 100-280nm

    UV-a goes through the glass and is a major one
    UV-b account for only 3% of UV radiation
    UV-c is absorbed by ozone layer


  • Hardware Contributor

    @alexsh1 so if i were you i would have bought a veml6075 when you ordered at some ic supplier! I'm joking.. or not ;)


  • Hero Member

    Anyone care to share a sketch that includes the VEML6075?



  • @korttoma I have not received VEML6075 yet so cannot help you


  • Hardware Contributor

    @korttoma what do you need exactly?? I can share some example, but there is nothing fancy for veml6075. On my side i'm using this lib for the moment https://github.com/schizobovine/VEML6075 , not digged much yet but it's working with MySensors of course. You simply need the same cooking in your sketch, as usual ;)


  • Hero Member

    Well, I'm looking to replace my Oregon Scientific UV Sensor UVN800 (that finally stopped reporting any values) by using a VEML6075 and a Sensebender Micro.

    I can probably work out the code for the sketch myself but I'm not a fan of reinventing the wheel so if someone already created a sketch it would simply save me the time.

    I would expect the sensor to report a UV Index value between 0 and 10 like the Oregon sensor did according to this -> https://www.epa.gov/sunsafety/uv-index-scale-1

    The example in the Lib you mentioned @scalz seems to have the UV Index posibility



  • @korttoma this thread is about VEML6075 :-)
    However, VEML6075 is more complex - it does report uv-a and uv-b. I do not think you'd get UVI from this sensor straight away, you'd need to do it in the sketch.

    I can share the sketch after I get the sensor, write a code and convert it into MySensors


  • Hardware Contributor

    @korttoma oki, i agree with you about reinventing the wheel, but for big job. I think it's always better to know how to use the lib, so that's not a waste imho ;)

    No time for the moment to check my bigger sketch and extract everything.

    But in case you would like to try your new sensor, here a quick 5-10min conversion and mix of the lib example to mysensors uv. I wouldn't release it like this, not optimized, and untested version. But it's compiling, it should work :)

    // Enable debug prints to serial monitor
    #define MY_DEBUG
    
    // Enable and select radio type attached
    #define MY_RADIO_NRF24
    //#define MY_RADIO_RFM69
    
    //#define MY_NODE_ID 10
    
    #include <MySensors.h>
    
    #include <VEML6075.h>
    
    
    VEML6075 veml6075 = VEML6075();
    bool sensorFound = false;
    
    #define CHILD_ID_UVI 0
    #define CHILD_ID_UVA 0
    #define CHILD_ID_UVB 0
    
    uint32_t SLEEP_TIME = 30*1000; // Sleep time between reads (in milliseconds)
    
    MyMessage uviMsg(CHILD_ID_UVI, V_UV);
    MyMessage uvaMsg(CHILD_ID_UVA, V_UV);
    MyMessage uvbMsg(CHILD_ID_UVB, V_UV);
    
    
    void presentation()
    {
        // Send the sketch version information to the gateway and Controller
        sendSketchInfo("UV Sensor", "1.3");
    
        // Register all sensors to gateway (they will be created as child devices)
        present(CHILD_ID_UVI, S_UV);
        present(CHILD_ID_UVA, S_UV);
        present(CHILD_ID_UVB, S_UV);
    }
    
    void setup()
    {
      if (!veml6075.begin()) {
        Serial.println(F("VEML6075 not found!"));
      } else 
        sensorFound = true;
    
    }
    
    void loop()
    {
      if (sensorFound) {
        unsigned long lastSend =0;
        float uvi;
        float uva;
        float uvb;
        static float uviOld = -1;
        static float uvaOld = -1;
        static float uvbOld = -1;
    
        // Poll sensor
        veml6075.poll();
    
        uva = veml6075.getUVA();
        Serial.print(F("UVA = "));
        Serial.println(uva, 2);
    
        uvb = veml6075.getUVB();
        Serial.print(F("UVB = "));
        Serial.println(uvb, 2);
    
        uvi = veml6075.getUVIndex();
        Serial.print(F("UV Index = "));
        Serial.println(uvi, 1);
    
        uint16_t devid = veml6075.getDevID();
        Serial.print(F("Device ID = "));
        Serial.println(devid, HEX);
    
        Serial.println(F("----------------"));
        
        if (uvi != uviOld) {
            send(uviMsg.set(uvi,2));
            uviOld = uvi;
        }
        if (uva != uvaOld) {
            send(uvaMsg.set(uva,2));
            uvaOld = uva;
        }
        if (uvb != uvbOld) {
            send(uvbMsg.set(uvb,2));
            uvbOld = uvb;
        }    
      }
    
      sleep(SLEEP_TIME);
    }
    

    It simply read sensor, send messages, and sleep.

    As you can see, it's possible to get the three values from the lib.


  • Hero Member

    Thanks for the sketch @scalz , I made an attempt at it myself also. I was not avare that V_UV and S_UV could be used so I had V/S_LIGHT_LEVEL but I changed it now. Seems to compile just fine. Just need to get it tested.

    SensebenderMicroWithUVIndex:

    /**
     * The MySensors Arduino library handles the wireless radio link and protocol
     * between your home built sensors/actuators and HA controller of choice.
     * The sensors forms a self healing radio network with optional repeaters. Each
     * repeater and gateway builds a routing tables in EEPROM which keeps track of the
     * network topology allowing messages to be routed to nodes.
     *
     * Created by Henrik Ekblad <henrik.ekblad@mysensors.org>
     * Copyright (C) 2013-2015 Sensnology AB
     * Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors
     *
     * Documentation: http://www.mysensors.org
     * Support Forum: http://forum.mysensors.org
     *
     * This program is free software; you can redistribute it and/or
     * modify it under the terms of the GNU General Public License
     * version 2 as published by the Free Software Foundation.
     *
     *******************************
     *
     * REVISION HISTORY
     * Version 1.0 - Thomas Bowman Mørch
     * 
     * DESCRIPTION
     * Default sensor sketch for Sensebender Micro module
     * Act as a temperature / humidity sensor by default.
     *
     * If A0 is held low while powering on, it will enter testmode, which verifies all on-board peripherals
     *  
     * Battery voltage is as battery percentage (Internal message), and optionally as a sensor value (See defines below)
     *
     *
     * Version 1.3 - Thomas Bowman Mørch
     * Improved transmission logic, eliminating spurious transmissions (when temperatuere / humidity fluctuates 1 up and down between measurements) 
     * Added OTA boot mode, need to hold A1 low while applying power. (uses slightly more power as it's waiting for bootloader messages)
     * 
     * Version 1.4 - Thomas Bowman Mørch
     * 
     * Corrected division in the code deciding whether to transmit or not, that resulted in generating an integer. Now it's generating floats as expected.
     * Simplified detection for OTA bootloader, now detecting if MY_OTA_FIRMWARE_FEATURE is defined. If this is defined sensebender automaticly waits 300mS after each transmission
     * Moved Battery status messages, so they are transmitted together with normal sensor updates (but only every 60th minute)
     * 
     */
    
    // Enable debug prints to serial monitor
    //#define MY_DEBUG 
    
    // Define a static node address, remove if you want auto address assignment
    #define MY_NODE_ID 27
    
    // Enable and select radio type attached
    #define MY_RADIO_NRF24
    //#define MY_RADIO_RFM69
    
    // Enable to support OTA for this node (needs DualOptiBoot boot-loader to fully work)
    //#define MY_OTA_FIRMWARE_FEATURE
    
    #include <SPI.h>
    #include <MySensors.h>
    #include <Wire.h>
    #include <SI7021.h>
    #ifndef MY_OTA_FIRMWARE_FEATURE
    #include "drivers/SPIFlash/SPIFlash.cpp"
    #endif
    #include <EEPROM.h>  
    #include <sha204_lib_return_codes.h>
    #include <sha204_library.h>
    #include <RunningAverage.h>
    #include <VEML6075.h>
    //#include <avr/power.h>
    
    // Uncomment the line below, to transmit battery voltage as a normal sensor value
    //#define BATT_SENSOR    199
    
    #define RELEASE "1.4"
    
    #define AVERAGES 2
    
    // Child sensor ID's
    #define CHILD_ID_UVI    1
    #define CHILD_ID_TEMP   2
    #define CHILD_ID_HUM    3
    
    // How many milli seconds between each measurement
    #define MEASURE_INTERVAL 60000
    
    // How many milli seconds should we wait for OTA?
    #define OTA_WAIT_PERIOD 300
    
    // FORCE_TRANSMIT_INTERVAL, this number of times of wakeup, the sensor is forced to report all values to the controller
    #define FORCE_TRANSMIT_INTERVAL 30 
    
    // When MEASURE_INTERVAL is 60000 and FORCE_TRANSMIT_INTERVAL is 30, we force a transmission every 30 minutes.
    // Between the forced transmissions a tranmission will only occur if the measured value differs from the previous measurement
    
    // HUMI_TRANSMIT_THRESHOLD tells how much the humidity should have changed since last time it was transmitted. Likewise with
    // TEMP_TRANSMIT_THRESHOLD for temperature threshold.
    #define HUMI_TRANSMIT_THRESHOLD 0.5
    #define TEMP_TRANSMIT_THRESHOLD 0.5
    #define UVI_TRANSMIT_THRESHOLD 0.1
    
    // Pin definitions
    #define TEST_PIN       A0
    #define LED_PIN        A2
    #define ATSHA204_PIN   17 // A3
    
    VEML6075 veml6075 = VEML6075();
    
    const int sha204Pin = ATSHA204_PIN;
    atsha204Class sha204(sha204Pin);
    
    SI7021 humiditySensor;
    SPIFlash flash(8, 0x1F65);
    
    // Sensor messages
    MyMessage msgHum(CHILD_ID_HUM, V_HUM);
    MyMessage msgTemp(CHILD_ID_TEMP, V_TEMP);
    MyMessage msgUVI(CHILD_ID_UVI, V_UV);
    
    #ifdef BATT_SENSOR
    MyMessage msgBatt(BATT_SENSOR, V_VOLTAGE);
    #endif
    
    // Global settings
    int measureCount = 0;
    int sendBattery = 0;
    boolean isMetric = true;
    boolean highfreq = true;
    boolean transmission_occured = false;
    
    // Storage of old measurements
    float lastTemperature = -100;
    int lastHumidity = -100;
    long lastBattery = -100;
    int lastUVI = -100;
    
    RunningAverage raHum(AVERAGES);
    
    /****************************************************
     *
     * Setup code 
     *
     ****************************************************/
    void setup() {
      
      pinMode(LED_PIN, OUTPUT);
      digitalWrite(LED_PIN, LOW);
    
      //Serial.begin(115200);
      //Serial.print(F("Sensebender Micro FW "));
      //Serial.print(RELEASE);
      //Serial.flush();
    
      // First check if we should boot into test mode
    
      pinMode(TEST_PIN,INPUT);
      digitalWrite(TEST_PIN, HIGH); // Enable pullup
      if (!digitalRead(TEST_PIN)) testMode();
    
      // Make sure that ATSHA204 is not floating
      pinMode(ATSHA204_PIN, INPUT);
      digitalWrite(ATSHA204_PIN, HIGH);
      
      digitalWrite(TEST_PIN,LOW);
      
      digitalWrite(LED_PIN, HIGH); 
    
      humiditySensor.begin();
    
      veml6075.begin();
      
      digitalWrite(LED_PIN, LOW);
    
      //Serial.flush();
      //Serial.println(F(" - Online!"));
      
      isMetric = getControllerConfig().isMetric;
      //Serial.print(F("isMetric: ")); Serial.println(isMetric);
      raHum.clear();
      sendTempHumidityMeasurements(false);
      sendBattLevel(false);
      
    #ifdef MY_OTA_FIRMWARE_FEATURE  
      //Serial.println("OTA FW update enabled");
    #endif
    
    }
    
    void presentation()  {
      sendSketchInfo("Sensebender Micro", RELEASE);
    
      present(CHILD_ID_UVI,S_UV);
      present(CHILD_ID_TEMP,S_TEMP);
      present(CHILD_ID_HUM,S_HUM);
        
    #ifdef BATT_SENSOR
      present(BATT_SENSOR, S_POWER);
    #endif
    }
    
    
    /***********************************************
     *
     *  Main loop function
     *
     ***********************************************/
    void loop() {
      
      measureCount ++;
      sendBattery ++;
      bool forceTransmit = false;
      transmission_occured = false;
    #ifndef MY_OTA_FIRMWARE_FEATURE
      if ((measureCount == 5) && highfreq) 
      {
        clock_prescale_set(clock_div_8); // Switch to 1Mhz for the reminder of the sketch, save power.
        highfreq = false;
      } 
    #endif
      
      if (measureCount > FORCE_TRANSMIT_INTERVAL) { // force a transmission
        forceTransmit = true; 
        measureCount = 0;
      }
        
      sendTempHumidityMeasurements(forceTransmit);
    /*  if (sendBattery > 60) 
      {
         sendBattLevel(forceTransmit); // Not needed to send battery info that often
         sendBattery = 0;
      }*/
    #ifdef MY_OTA_FIRMWARE_FEATURE
      if (transmission_occured) {
          wait(OTA_WAIT_PERIOD);
      }
    #endif
    
      sleep(MEASURE_INTERVAL);  
    }
    
    
    /*********************************************
     *
     * Sends temperature and humidity from Si7021 sensor
     *
     * Parameters
     * - force : Forces transmission of a value (even if it's the same as previous measurement)
     *
     *********************************************/
    void sendTempHumidityMeasurements(bool force)
    {
      bool tx = force;
    
      si7021_env data = humiditySensor.getHumidityAndTemperature();
    
      veml6075.poll();
    
        float UVA = veml6075.getUVA();
        //Serial.print(F("UVA = "));
        //Serial.println(UVA, 2);
    
        float UVB = veml6075.getUVB();
        //Serial.print(F("UVB = "));
        //Serial.println(UVB, 2);
    
        float UVI = veml6075.getUVIndex();
        //Serial.print(F("UV Index = "));
        //Serial.println(UVI, 1);
      
      
      
      raHum.addValue(data.humidityPercent);
      
      float diffTemp = abs(lastTemperature - (isMetric ? data.celsiusHundredths : data.fahrenheitHundredths)/100.0);
      float diffHum = abs(lastHumidity - raHum.getAverage());
    
      float diffUVI = abs(lastUVI - UVI);
    
      //Serial.print(F("TempDiff :"));Serial.println(diffTemp);
      //Serial.print(F("HumDiff  :"));Serial.println(diffHum); 
      //Serial.print(F("UVIDiff  :"));Serial.println(diffUVI); 
      
      if (isnan(diffHum)) tx = true; 
      if (diffTemp > TEMP_TRANSMIT_THRESHOLD) tx = true;
      if (diffHum > HUMI_TRANSMIT_THRESHOLD) tx = true;
      if (diffUVI > UVI_TRANSMIT_THRESHOLD) tx = true;
    
      
      if (tx) {
        measureCount = 0;
        float temperature = (isMetric ? data.celsiusHundredths : data.fahrenheitHundredths) / 100.0;
         
        int humidity = data.humidityPercent;
        //Serial.print("T: ");Serial.println(temperature);
        //Serial.print("H: ");Serial.println(humidity);
        
        send(msgTemp.set(temperature,1));
        send(msgHum.set(humidity));
        send(msgUVI.set(UVI,2));
        lastTemperature = temperature;
        lastHumidity = humidity;
        lastUVI = UVI;
        transmission_occured = true;
        if (sendBattery > 60) {
         sendBattLevel(true); // Not needed to send battery info that often
         sendBattery = 0;
        }
      }
    }
    
    /********************************************
     *
     * Sends battery information (battery percentage)
     *
     * Parameters
     * - force : Forces transmission of a value
     *
     *******************************************/
    void sendBattLevel(bool force)
    {
      if (force) lastBattery = -1;
      long vcc = readVcc();
      if (vcc != lastBattery) {
        lastBattery = vcc;
    
    #ifdef BATT_SENSOR
        float send_voltage = float(vcc)/1000.0f;
        send(msgBatt.set(send_voltage,3));
    #endif
    
        // Calculate percentage
    
        vcc = vcc - 1900; // subtract 1.9V from vcc, as this is the lowest voltage we will operate at
        
        long percent = vcc / 14.0;
        sendBatteryLevel(percent);
        transmission_occured = true;
      }
    }
    
    /*******************************************
     *
     * Internal battery ADC measuring 
     *
     *******************************************/
    long readVcc() {
      // Read 1.1V reference against AVcc
      // set the reference to Vcc and the measurement to the internal 1.1V reference
      #if defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
        ADMUX = _BV(REFS0) | _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
      #elif defined (__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__)
        ADMUX = _BV(MUX5) | _BV(MUX0);
      #elif defined (__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__)
        ADcdMUX = _BV(MUX3) | _BV(MUX2);
      #else
        ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
      #endif  
     
      delay(2); // Wait for Vref to settle
      ADCSRA |= _BV(ADSC); // Start conversion
      while (bit_is_set(ADCSRA,ADSC)); // measuring
     
      uint8_t low  = ADCL; // must read ADCL first - it then locks ADCH  
      uint8_t high = ADCH; // unlocks both
     
      long result = (high<<8) | low;
     
      result = 1125300L / result; // Calculate Vcc (in mV); 1125300 = 1.1*1023*1000
      return result; // Vcc in millivolts
     
    }
    
    /****************************************************
     *
     * Verify all peripherals, and signal via the LED if any problems.
     *
     ****************************************************/
    void testMode()
    {
      uint8_t rx_buffer[SHA204_RSP_SIZE_MAX];
      uint8_t ret_code;
      byte tests = 0;
      
      digitalWrite(LED_PIN, HIGH); // Turn on LED.
      //Serial.println(F(" - TestMode"));
      //Serial.println(F("Testing peripherals!"));
      //Serial.flush();
      //Serial.print(F("-> SI7021 : ")); 
      //Serial.flush();
      
      if (humiditySensor.begin()) 
      {
        //Serial.println(F("ok!"));
        tests ++;
      }
      else
      {
        //Serial.println(F("failed!"));
      }
      //Serial.flush();
    
      //Serial.print(F("-> Flash : "));
      //Serial.flush();
      if (flash.initialize())
      {
        //Serial.println(F("ok!"));
        tests ++;
      }
      else
      {
        //Serial.println(F("failed!"));
      }
      //Serial.flush();
    
      
      //Serial.print(F("-> SHA204 : "));
      ret_code = sha204.sha204c_wakeup(rx_buffer);
      //Serial.flush();
      if (ret_code != SHA204_SUCCESS)
      {
        //Serial.print(F("Failed to wake device. Response: ")); Serial.println(ret_code, HEX);
      }
      //Serial.flush();
      if (ret_code == SHA204_SUCCESS)
      {
        ret_code = sha204.getSerialNumber(rx_buffer);
        if (ret_code != SHA204_SUCCESS)
        {
          //Serial.print(F("Failed to obtain device serial number. Response: ")); Serial.println(ret_code, HEX);
        }
        else
        {
          //Serial.print(F("Ok (serial : "));
          for (int i=0; i<9; i++)
          {
            if (rx_buffer[i] < 0x10)
            {
              //Serial.print('0'); // Because Serial.print does not 0-pad HEX
            }
            //Serial.print(rx_buffer[i], HEX);
          }
          //Serial.println(")");
          tests ++;
        }
    
      }
      //Serial.flush();
    
      //Serial.println(F("Test finished"));
      
      if (tests == 3) 
      {
        //Serial.println(F("Selftest ok!"));
        while (1) // Blink OK pattern!
        {
          digitalWrite(LED_PIN, HIGH);
          delay(200);
          digitalWrite(LED_PIN, LOW);
          delay(200);
        }
      }
      else 
      {
        //Serial.println(F("----> Selftest failed!"));
        while (1) // Blink FAILED pattern! Rappidly blinking..
        {
        }
      }  
    }
    


  • This is the VEML6070 UV Index graph. The last day, I placed the sensor outside rather than behind my window. There is a huge difference :

    0_1498067711576_Screenshot (33).png


Log in to reply
 

Looks like your connection to MySensors Forum was lost, please wait while we try to reconnect.