0.2.0 ML8511

This commit is contained in:
Rob Tillaart 2024-09-12 17:24:13 +02:00
parent 13f80f4903
commit 7fb98199e6
9 changed files with 267 additions and 110 deletions

View File

@ -6,10 +6,22 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).
## [0.2.0] - 2024-09-12
- add parameter check in **estimateDUVindex()**
- improve behaviour enable functions
- change return type **setVoltsPerStep()**
- change return type **enable()**
- change return type **disable()**
- reorder code in .cpp file to match .h file.
- add experimental example - ML8511_cumulative_joule.ino
- update readme.md
- minor edits
----
## [0.1.11] - 2023-11-13
- update readme.md
## [0.1.10] - 2023-02-18
- update readme.md
- update GitHub actions

View File

@ -1,7 +1,7 @@
//
// FILE: ML8511.cpp
// AUTHOR: Rob Tillaart
// VERSION: 0.1.11
// VERSION: 0.2.0
// DATE: 2020-02-03
// PURPOSE: ML8511 - UV sensor - library for Arduino
// URL: https://github.com/RobTillaart/ML8511
@ -35,7 +35,7 @@ ML8511::ML8511(uint8_t analogPin, uint8_t enablePin)
void ML8511::reset()
{
_voltsPerStep = 5.0/1023;
_voltsPerStep = 5.0 / 1023.0;
_DUVfactor = 1.61; // https://github.com/RobTillaart/ML8511/issues/4
}
@ -61,13 +61,13 @@ float ML8511::getUV(uint8_t energyMode)
}
// to be used by external ADC
// can be used by external ADC
float ML8511::voltage2mW(float voltage)
{
// see datasheet - page 4
// mW/cm2 @ 365 nm
// @ 25 Celsius
// formula estimated on graph
// formula estimated from graph
if (voltage <= 1.0)
{
return 0.0;
@ -78,6 +78,46 @@ float ML8511::voltage2mW(float voltage)
}
bool ML8511::setVoltsPerStep(float voltage, uint32_t steps)
{
if (steps == 0) return false;
if (voltage <= 0.0) return false;
_voltsPerStep = voltage / steps;
return true;
}
float ML8511::getVoltsPerStep()
{
return _voltsPerStep;
}
bool ML8511::enable()
{
if (_enablePin == 0xFF) return false;
digitalWrite(_enablePin, HIGH);
_enabled = true;
return true;
}
bool ML8511::disable()
{
if (_enablePin == 0xFF) return false;
digitalWrite(_enablePin, LOW);
_enabled = false;
return true;
}
bool ML8511::isEnabled()
{
return _enabled;
}
// experimental estimate DUV index ( ==> USE WITH CARE !!)
// use setDUVfactor(float w) to calibrate
//
@ -86,8 +126,9 @@ float ML8511::estimateDUVindex(float mWcm2)
{
// rewrite in 0.1.6
// https://github.com/RobTillaart/ML8511/issues/4
if (mWcm2 <= 0.0) return 0.0;
return mWcm2 * _DUVfactor;
};
}
bool ML8511::setDUVfactor(float factor)
@ -95,47 +136,14 @@ bool ML8511::setDUVfactor(float factor)
if (factor < 0.01) return false; // enforce positive values
_DUVfactor = factor;
return true;
};
}
float ML8511::getDUVfactor()
{
return _DUVfactor;
};
void ML8511::setVoltsPerStep(float voltage, uint32_t steps)
{
if (steps == 0) return;
if (voltage > 0.0) _voltsPerStep = voltage / steps;
}
float ML8511::getVoltsPerStep()
{
return _voltsPerStep;
}
void ML8511::enable()
{
if (_enablePin != 0xFF) digitalWrite(_enablePin, HIGH);
_enabled = true;
};
void ML8511::disable()
{
if (_enablePin != 0xFF) digitalWrite(_enablePin, LOW);
_enabled = false;
};
bool ML8511::isEnabled()
{
return _enabled;
};
// -- END OF FILE --

View File

@ -2,7 +2,7 @@
//
// FILE: ML8511.h
// AUTHOR: Rob Tillaart
// VERSION: 0.1.11
// VERSION: 0.2.0
// DATE: 2020-02-03
// PURPOSE: ML8511 - UV sensor - library for Arduino
// URL: https://github.com/RobTillaart/ML8511
@ -23,7 +23,7 @@
#include <Arduino.h>
#define ML8511_LIB_VERSION (F("0.1.11"))
#define ML8511_LIB_VERSION (F("0.2.0"))
class ML8511
@ -32,30 +32,34 @@ public:
// if enablePin is omitted, one must connect EN to 3V3.
ML8511(uint8_t analogPin, uint8_t enablePin = 0xFF);
void reset(); // reset internal variables to initial value.
void reset(); // reset internal variables to initial value.
// energyMode = HIGH or LOW;
// returns mW per cm2
float getUV(uint8_t energyMode = HIGH);
float getUV(uint8_t energyMode = HIGH);
// for external ADC
float voltage2mW(float voltage);
// voltage must be >= 1.0 otherwise 0 is returned.
// returns mW per cm2
float voltage2mW(float voltage);
// voltage must be > 0 otherwise it is not set
void setVoltsPerStep(float voltage, uint32_t steps);
float getVoltsPerStep();
// voltage and steps must both be > 0 otherwise voltagePerStep
// is not set and the function returns false.
bool setVoltsPerStep(float voltage, uint32_t steps);
float getVoltsPerStep();
// manually enable / disable
void enable();
void disable();
bool isEnabled();
// manually enable / disable the sensor
// if enable pin is not set, device is always enabled.
bool enable(); // returns false if enable pin not set.
bool disable(); // returns false if enable pin not set.
bool isEnabled();
// experimental estimate DUV index
// EXPERIMENTAL: estimate DUV index
// WARNING: USE WITH CARE
//
// input in mW per cm2 == typical the output of getUV()
float estimateDUVindex(float mWcm2);
// input in mW per cm2 == typical the output of getUV()
float estimateDUVindex(float mWcm2);
// https://github.com/RobTillaart/ML8511/issues/4
// discusses the calibration
@ -64,17 +68,17 @@ public:
// a value of 1.61 was found to be far more accurate
//
// returns false if f < 0.01 (to force positive factor only)
bool setDUVfactor(float factor);
float getDUVfactor();
bool setDUVfactor(float factor);
float getDUVfactor();
private:
uint8_t _analogPin;
uint8_t _enablePin;
float _voltsPerStep;
bool _enabled;
uint8_t _analogPin;
uint8_t _enablePin;
float _voltsPerStep;
bool _enabled;
float _DUVfactor;
float _DUVfactor;
};

View File

@ -0,0 +1,77 @@
//
// FILE: ML8511_cumulative_joule.ino
// AUTHOR: Rob Tillaart
// PURPOSE: demo UV sensor - EXPERIMENTAL
// URL: https://github.com/RobTillaart/ML8511
//
// BREAKOUT
// +-------+--+
// VIN |o +-+| mounting hole
// 3V3 |o +-+|
// GND |o |
// OUT |o |
// EN |o S | Sensor
// +----------+
//
// EN = ENABLE
#include "ML8511.h"
#define ANALOGPIN A0
#define ENABLEPIN 7
ML8511 light(ANALOGPIN, ENABLEPIN);
float joulePerCM = 0;
float interval = 2.5; // in seconds.
uint32_t lastTime = 0;
void setup()
{
Serial.begin(115200);
Serial.println();
Serial.println(__FILE__);
Serial.println();
Serial.println("UV ML8511 - cumulative joule/cm2");
// manually enable / disable the sensor.
light.enable();
light.setDUVfactor(1.80); // calibrate your sensor
Serial.println();
Serial.print("time");
Serial.print("\tmW/cm^2");
Serial.print("\tDUV idx");
Serial.print("\tcum j/cm^2");
Serial.println();
}
void loop()
{
// make a measurement and a print every interval.
if (millis() - lastTime >= (interval * 1000))
{
lastTime += (interval * 1000);
float UV = light.getUV();
float DUV = light.estimateDUVindex(UV);
joulePerCM += UV * interval;
Serial.print(lastTime * 0.001, 1);
Serial.print("\t");
Serial.print(UV, 3);
Serial.print("\t");
Serial.print(DUV, 1);
Serial.print("\t");
Serial.print(joulePerCM, 1);
Serial.println();
}
}
// -- END OF FILE --

View File

@ -9,6 +9,7 @@ reset KEYWORD2
getUV KEYWORD2
voltage2mW KEYWORD2
setVoltsPerStep KEYWORD2
getVoltsPerStep KEYWORD2

View File

@ -15,7 +15,7 @@
"type": "git",
"url": "https://github.com/RobTillaart/ML8511.git"
},
"version": "0.1.11",
"version": "0.2.0",
"license": "MIT",
"frameworks": "*",
"platforms": "*",

View File

@ -1,5 +1,5 @@
name=ML8511
version=0.1.11
version=0.2.0
author=Rob Tillaart <rob.tillaart@gmail.com>
maintainer=Rob Tillaart <rob.tillaart@gmail.com>
sentence=ML8511 - UV sensor - library for Arduino

View File

@ -16,7 +16,7 @@ Arduino library for the ML8511 UV sensor.
## Warning
**Always take precautions as UV radiation can cause sunburn, eye damage and other problems**.
**Always take precautions as UV radiation can cause sunburn, eye damage and other severe problems**.
Do not expose yourself to the sun as UV source too long.
@ -31,15 +31,22 @@ ML8511 - UV sensor - library for Arduino UNO.
- do not forget to connect the EN to either an enablePIN or to 3V3 (constantly enabled).
#### Related
Use of an external ADC see below.
- https://github.com/RobTillaart/TSL235R pulse based irradiance variant.
- https://github.com/RobTillaart/TSL260R analog IR irradiance variant.
### Related
- https://github.com/RobTillaart/TSL235R pulse based irradiance variant.
- https://github.com/RobTillaart/TSL260R analog IR irradiance variant.
- https://github.com/RobTillaart/AnalogUVSensor
- https://github.com/RobTillaart/LTR390_DFR UV sensor (DF Robotics edition)
- https://github.com/RobTillaart/LTR390_RT UV sensor
- https://github.com/RobTillaart/ML8511 UV sensor
- https://learn.sparkfun.com/tutorials/ml8511-uv-sensor-hookup-guide
- https://en.wikipedia.org/wiki/Ultraviolet_index
#### Breakout
### Breakout
```
// +-------+--+
@ -81,32 +88,50 @@ Use a voltage divider (10K + 20K) to convert the 5 Volts to ~3.3 Volts.
#include "ML8511.h"
```
#### Constructor
### Constructor
- **ML8511(uint8_t analogPin, uint8_t enablePin = 0xFF)** Constructor,
if enable is connected to 3V3 constantly one does not need to set the enablePin parameter.
#### Core
### Core
- **float getUV(uint8_t energyMode = HIGH)** returns mW per cm2, energyMode = HIGH or LOW.
LOW will disable the sensor after each read.
energyMode = LOW will disable the sensor after each read.
Function blocks for (at least) 1 millisecond if sensor is disabled.
- **float voltage2mW(float voltage)** returns mW per cm2 from voltage.
To be used when one uses an external ADC e.g. ADS1115
- **void setVoltsPerStep(float voltage, uint32_t steps)** to calibrate the **internal** ADC used.
Voltage must be > 0 otherwise it is not set and the default of 5 volts 1023 steps is used.
Can be used when one uses an external ADC e.g. ADS1115
Formula is based upon the graph in the datasheet page 4 (at 25 Celsius)
- **bool setVoltsPerStep(float voltage, uint32_t steps)** to calibrate the **internal** ADC used.
If one of the parameters voltage or steps is <= 0 the function returns false.
Then the previous set value or the default of 5 volts 1023 steps is used.
This function has no meaning for an external ADC.
- **float getVoltsPerStep()** idem.
- **void enable()** manually enable.
- **void disable()** manually disable.
- **bool isEnabled()** get enabled status.
#### Experimental
### Enable / disable
These functions only work if the enable pin is set in the constructor.
- **bool enable()** manually enable the device.
Returns false if enable pin not set.
- **bool disable()** manually disable the device.
Returns false if enable pin not set.
- **bool isEnabled()** returns the enabled status.
Returns true if enable pin not set as it cannot be disabled.
### Experimental
WARNING: USE WITH CARE
- **float estimateDUVindex(float mWcm2)** input in mW per cm2, returns a value between 0 and ~15(?)
- **void setDUVfactor(float factor)** set the conversion factor
- **float estimateDUVindex(float mWcm2)** input in mW per cm2.
Typically returns a value between 0 and ~15(?)
Returns zero if the parameter mWcm2 <= 0.0.
- **bool setDUVfactor(float factor)** set the conversion factor.
Returns false if the factor < 0.01 (hard coded minimum).
This factor can be used to sort of calibrate a system if a medium
(e.g. glass) is between sensor and UV source
- **float getDUVfactor()** returns the set conversion factor (default 1.61)
See below (Experimental DUVindex) how to determine the DUV factor for your sensor.
@ -114,8 +139,10 @@ See below (Experimental DUVindex) how to determine the DUV factor for your senso
_Note:
The UV index can be very high, in La Paz, Bolivia, one of the highest cities in the world
the DUV index can go above 20. See link below.
This is really extreme and it is unknown how the ML8511 sensor (and this library) behaves under such conditions, and how long the sensor would survive.
Datasheet goes up to 15 mW per cm2, with a default DUVfactor of ~1.61 the measurements could handle DUV of ~24 in theory._
This is really extreme and it is unknown how the ML8511 sensor (and this library)
behaves under such conditions, and how long the sensor would survive.
Datasheet goes up to 15 mW per cm2, with a default DUVfactor of ~1.61
the measurements could handle DUV of ~24 in theory._
https://edition.cnn.com/2021/11/03/americas/bolivia-heatwave-highlands-intl/index.html
@ -140,21 +167,26 @@ Note: this library is **NOT** calibrated so **USE AT OWN RISK**
The DUV index can be used for warning for sunburn etc.
#### DUV index table
### DUV index table
Based upon https://en.wikipedia.org/wiki/Ultraviolet_index,
| DUV INDEX | Description |
|:-----------:|:--------------|
| 0 - 2 | LOW |
| 3 - 5 | MODERATE |
| 6 - 7 | HIGH |
| 8 - 10 | VERY HIGH |
| 11+ | EXTREME |
Based upon https://en.wikipedia.org/wiki/Ultraviolet_index,
| DUV INDEX | Description | Colour |
|:-----------:|:--------------|:---------|
| 0 - 2 | LOW | GREEN |
| 3 - 5 | MODERATE | YELLOW |
| 6 - 7 | HIGH | ORANGE |
| 8 - 10 | VERY HIGH | RED |
| 11+ | EXTREME | PURPLE |
#### Calibrate estimateDUVindex()
Colour codes are indicative to be used in a user interface.
A more elaborated colour scheme may be made with map2colour.
- https://github.com/RobTillaart/map2colour
### Calibrate estimateDUVindex()
To calibrate the **estimateDUVindex()** function one needs to determine the DUVfactor.
To do this you need an external reference e.g. a local or nearby weather station.
@ -162,21 +194,25 @@ You need to make multiple measurements during the (preferably unclouded) day and
calculate the factor.
```
DUV from weather station
factor = --------------------------
getUV();
DUV from weather station
DUVfactor = --------------------------
getUV();
```
you do this e.g. once per hour, so you get multiple values.
You do this e.g. once per hour, so you get multiple values.
You can then average them to have a single factor.
Hardcode this found value in the library (in the constructor) or better
use the **setDUVfactor(factor)** call in **setup()** to calibrate your sensor.
It might be useful to calibrate the DUV factor on different moments of the
day to correct for the angle of inclination. One might need to adjust this
during measurements by using e.g. an RTC = real time clock.
## Version info
#### 0.1.5 and before
### 0.1.5 and before
The formula for the experimental **estimateDUVindex(mWcm2)** is based on
the following facts / assumptions:
@ -188,7 +224,7 @@ This is the most lethal the sensor can sense > 80%.
(Erythemal action spectrum)
As we cannot differentiate wavelengths, this is the safest choice.
#### 0.1.6
### 0.1.6
The formula is simplified to a single factor that the user needs to determine.
Below is described how to do the calibration.
@ -216,18 +252,24 @@ https://en.wikipedia.org/wiki/Ultraviolet_index
#### Should
- test more
- get unit tests up and running
- investigate in calibration
- check performance
- test more platforms
- investigate in calibration
#### Could
- investigate serial UV communication with UV led
- voltage2mW -> handle negative voltages by taking abs value?
- get unit tests up and running
- math for duration of exposure
- Converting from mW/cm2 ==> Joule / s / cm2
- integrate sum of multiple measurements.
- experimental example?
#### Wont
- investigate serial communication with UV led and UV sensor
- however fun experiment.
- check performance (mainly ADC dependent)
- add base class without enable code?
## Support

View File

@ -49,7 +49,7 @@ unittest_teardown()
#define ANALOGPIN 0
#define ENABLEPIN 10
unittest(test_constructor)
{
@ -62,6 +62,18 @@ unittest(test_constructor)
light.reset();
assertEqualFloat(5.0/1023, light.getVoltsPerStep(), 0.0001);
assertTrue(light.isEnabled());
light.disable();
assertTrue(light.isEnabled()); // always true without ENABLEPIN set
light.enable();
assertTrue(light.isEnabled());
}
unittest(test_constructor_2)
{
ML8511 light(ANALOGPIN, ENABLEPIN); // explicit ENABLEPIN
assertTrue(light.isEnabled());
light.disable();
assertFalse(light.isEnabled());
@ -69,6 +81,7 @@ unittest(test_constructor)
assertTrue(light.isEnabled());
}
/*
unittest(test_getUV)
{
@ -171,8 +184,8 @@ unittest(test_estimateDUVindex)
light.enable();
// output a table
fprintf(stderr, "mW\tDUV\n");
for (float mW = 0; mW < 10; mW += 0.5)
fprintf(stderr, "mW/cm2\tDUV\n");
for (float mW = 0; mW < 10.5 ; mW += 0.5)
{
fprintf(stderr, "%f\t", mW);
fprintf(stderr, "%f\n", light.estimateDUVindex(mW));