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/). 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 ## [0.1.11] - 2023-11-13
- update readme.md - update readme.md
## [0.1.10] - 2023-02-18 ## [0.1.10] - 2023-02-18
- update readme.md - update readme.md
- update GitHub actions - update GitHub actions

View File

@ -1,7 +1,7 @@
// //
// FILE: ML8511.cpp // FILE: ML8511.cpp
// AUTHOR: Rob Tillaart // AUTHOR: Rob Tillaart
// VERSION: 0.1.11 // VERSION: 0.2.0
// DATE: 2020-02-03 // DATE: 2020-02-03
// PURPOSE: ML8511 - UV sensor - library for Arduino // PURPOSE: ML8511 - UV sensor - library for Arduino
// URL: https://github.com/RobTillaart/ML8511 // URL: https://github.com/RobTillaart/ML8511
@ -35,7 +35,7 @@ ML8511::ML8511(uint8_t analogPin, uint8_t enablePin)
void ML8511::reset() void ML8511::reset()
{ {
_voltsPerStep = 5.0/1023; _voltsPerStep = 5.0 / 1023.0;
_DUVfactor = 1.61; // https://github.com/RobTillaart/ML8511/issues/4 _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) float ML8511::voltage2mW(float voltage)
{ {
// see datasheet - page 4 // see datasheet - page 4
// mW/cm2 @ 365 nm // mW/cm2 @ 365 nm
// @ 25 Celsius // @ 25 Celsius
// formula estimated on graph // formula estimated from graph
if (voltage <= 1.0) if (voltage <= 1.0)
{ {
return 0.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 !!) // experimental estimate DUV index ( ==> USE WITH CARE !!)
// use setDUVfactor(float w) to calibrate // use setDUVfactor(float w) to calibrate
// //
@ -86,8 +126,9 @@ float ML8511::estimateDUVindex(float mWcm2)
{ {
// rewrite in 0.1.6 // rewrite in 0.1.6
// https://github.com/RobTillaart/ML8511/issues/4 // https://github.com/RobTillaart/ML8511/issues/4
if (mWcm2 <= 0.0) return 0.0;
return mWcm2 * _DUVfactor; return mWcm2 * _DUVfactor;
}; }
bool ML8511::setDUVfactor(float factor) bool ML8511::setDUVfactor(float factor)
@ -95,47 +136,14 @@ bool ML8511::setDUVfactor(float factor)
if (factor < 0.01) return false; // enforce positive values if (factor < 0.01) return false; // enforce positive values
_DUVfactor = factor; _DUVfactor = factor;
return true; return true;
}; }
float ML8511::getDUVfactor() float ML8511::getDUVfactor()
{ {
return _DUVfactor; 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 -- // -- END OF FILE --

View File

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

View File

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

View File

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

View File

@ -16,7 +16,7 @@ Arduino library for the ML8511 UV sensor.
## Warning ## 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. 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). - 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/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://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" #include "ML8511.h"
``` ```
#### Constructor ### Constructor
- **ML8511(uint8_t analogPin, uint8_t enablePin = 0xFF)** 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. 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. - **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. - **float voltage2mW(float voltage)** returns mW per cm2 from voltage.
To be used when one uses an external ADC e.g. ADS1115 Can be used when one uses an external ADC e.g. ADS1115
- **void setVoltsPerStep(float voltage, uint32_t steps)** to calibrate the **internal** ADC used. Formula is based upon the graph in the datasheet page 4 (at 25 Celsius)
Voltage must be > 0 otherwise it is not set and the default of 5 volts 1023 steps is used. - **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. This function has no meaning for an external ADC.
- **float getVoltsPerStep()** idem. - **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 WARNING: USE WITH CARE
- **float estimateDUVindex(float mWcm2)** input in mW per cm2, returns a value between 0 and ~15(?) - **float estimateDUVindex(float mWcm2)** input in mW per cm2.
- **void setDUVfactor(float factor)** set the conversion factor 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) - **float getDUVfactor()** returns the set conversion factor (default 1.61)
See below (Experimental DUVindex) how to determine the DUV factor for your sensor. 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: _Note:
The UV index can be very high, in La Paz, Bolivia, one of the highest cities in the world 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. 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. This is really extreme and it is unknown how the ML8511 sensor (and this library)
Datasheet goes up to 15 mW per cm2, with a default DUVfactor of ~1.61 the measurements could handle DUV of ~24 in theory._ 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 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. 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, 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 |
| 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 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. 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. calculate the factor.
``` ```
DUV from weather station DUV from weather station
factor = -------------------------- DUVfactor = --------------------------
getUV(); 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. You can then average them to have a single factor.
Hardcode this found value in the library (in the constructor) or better Hardcode this found value in the library (in the constructor) or better
use the **setDUVfactor(factor)** call in **setup()** to calibrate your sensor. 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 ## Version info
#### 0.1.5 and before ### 0.1.5 and before
The formula for the experimental **estimateDUVindex(mWcm2)** is based on The formula for the experimental **estimateDUVindex(mWcm2)** is based on
the following facts / assumptions: the following facts / assumptions:
@ -188,7 +224,7 @@ This is the most lethal the sensor can sense > 80%.
(Erythemal action spectrum) (Erythemal action spectrum)
As we cannot differentiate wavelengths, this is the safest choice. 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. The formula is simplified to a single factor that the user needs to determine.
Below is described how to do the calibration. Below is described how to do the calibration.
@ -216,18 +252,24 @@ https://en.wikipedia.org/wiki/Ultraviolet_index
#### Should #### Should
- test more - test more platforms
- get unit tests up and running - investigate in calibration
- investigate in calibration
- check performance
#### Could #### Could
- investigate serial UV communication with UV led - get unit tests up and running
- voltage2mW -> handle negative voltages by taking abs value? - math for duration of exposure
- Converting from mW/cm2 ==> Joule / s / cm2
- integrate sum of multiple measurements.
- experimental example?
#### Wont #### 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 ## Support

View File

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