mirror of
https://github.com/RobTillaart/Arduino.git
synced 2024-10-03 18:09:02 -04:00
0.1.6 ML8511
This commit is contained in:
parent
1970f32c14
commit
a750333d50
@ -1,16 +1,19 @@
|
||||
//
|
||||
// FILE: ML8511.cpp
|
||||
// AUTHOR: Rob.Tillaart@gmail.com
|
||||
// VERSION: 0.1.5
|
||||
// VERSION: 0.1.6
|
||||
// PURPOSE: ML8511 - UV sensor - library for Arduino
|
||||
//
|
||||
// HISTORY:
|
||||
// 0.1.0 2020-02-03 initial version
|
||||
// 0.1.1 2020-02-17 added _voltPerStep() to support more boards
|
||||
// 0.1.2 2020-06-21 refactor; add estimateDUVindex()
|
||||
// 0.1.3 2021-01-01 arduino-ci + unit test
|
||||
// 0.1.3 2021-01-01 Arduino-ci + unit test
|
||||
// 0.1.4 2021-04-23 fix for platformIO
|
||||
// 0.1.5 2021-05-27 fix arduino-lint
|
||||
// 0.1.5 2021-05-27 fix Arduino-lint
|
||||
// 0.1.6 2021-06-19 add get/setDUVfactor(),
|
||||
// rewrite estimateDUVindex(),
|
||||
// add reset();
|
||||
|
||||
|
||||
#include "ML8511.h"
|
||||
@ -22,9 +25,8 @@
|
||||
//
|
||||
ML8511::ML8511(uint8_t analogPin, uint8_t enablePin)
|
||||
{
|
||||
_analogPin = analogPin;
|
||||
_voltsPerStep = 5.0/1023;
|
||||
_enablePin = enablePin;
|
||||
_analogPin = analogPin;
|
||||
_enablePin = enablePin;
|
||||
if (enablePin != 0xFF)
|
||||
{
|
||||
pinMode(_enablePin, OUTPUT);
|
||||
@ -36,9 +38,17 @@ ML8511::ML8511(uint8_t analogPin, uint8_t enablePin)
|
||||
{
|
||||
_enabled = true;
|
||||
}
|
||||
reset();
|
||||
};
|
||||
|
||||
|
||||
void ML8511::reset()
|
||||
{
|
||||
_voltsPerStep = 5.0/1023;
|
||||
_DUVfactor = 1.61; // https://github.com/RobTillaart/ML8511/issues/4
|
||||
}
|
||||
|
||||
|
||||
float ML8511::getUV(uint8_t energyMode)
|
||||
{
|
||||
if (!_enabled)
|
||||
@ -57,7 +67,7 @@ float ML8511::getUV(uint8_t energyMode)
|
||||
|
||||
// see datasheet - page 4
|
||||
// mW/cm2 @ 365 nm
|
||||
// @ 25 Celcius
|
||||
// @ 25 Celsius
|
||||
// formula estimated on graph
|
||||
if (voltage <= 1.0)
|
||||
{
|
||||
@ -69,18 +79,23 @@ float ML8511::getUV(uint8_t energyMode)
|
||||
}
|
||||
|
||||
|
||||
// experimental estimate DUV index (not calibrated, USE WITH CARE !!)
|
||||
// experimental estimate DUV index ( ==> USE WITH CARE !!)
|
||||
// use setDUVfactor(float w) to calibrate
|
||||
//
|
||||
// input is power in mW per cm2
|
||||
// weight is pretty high
|
||||
float ML8511::estimateDUVindex(float mWcm2)
|
||||
{
|
||||
float weight = 1.0; // this can be tuned to callibrate
|
||||
// rewrite in 0.1.6
|
||||
// https://github.com/RobTillaart/ML8511/issues/4
|
||||
return mWcm2 * _DUVfactor;
|
||||
};
|
||||
|
||||
// convert to mW per m2
|
||||
float mWm2 = mWcm2 * 10000;
|
||||
// factor to normalize to an 0..10 scale see Wikipedia.
|
||||
float factor = 0.04; // 1.0/25;
|
||||
return mWm2 * weight * factor;
|
||||
|
||||
bool ML8511::setDUVfactor(float f)
|
||||
{
|
||||
if (f < 0.01) return false; // enforce positive values
|
||||
_DUVfactor = f;
|
||||
return true;
|
||||
};
|
||||
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
//
|
||||
// FILE: ML8511.h
|
||||
// AUTHOR: Rob Tillaart
|
||||
// VERSION: 0.1.5
|
||||
// VERSION: 0.1.6
|
||||
// PURPOSE: ML8511 - UV sensor - library for Arduino
|
||||
// URL: https://github.com/RobTillaart/ML8511
|
||||
//
|
||||
@ -18,7 +18,7 @@
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#define ML8511_LIB_VERSION (F("0.1.5"))
|
||||
#define ML8511_LIB_VERSION (F("0.1.6"))
|
||||
|
||||
|
||||
class ML8511
|
||||
@ -27,14 +27,12 @@ public:
|
||||
// if enablePin is omitted, one must connect EN to 3V3.
|
||||
ML8511(uint8_t analogPin, uint8_t enablePin = 0xFF);
|
||||
|
||||
void reset(); // reset internal vars to initial value.
|
||||
|
||||
// energyMode = HIGH or LOW;
|
||||
// returns mW per cm2
|
||||
float getUV(uint8_t energyMode = HIGH);
|
||||
|
||||
// experimental estimate DUV index (not calibrated, USE WITH CARE !!)
|
||||
// input in mW per cm2
|
||||
float estimateDUVindex(float mWcm2);
|
||||
|
||||
// voltage must be > 0 otherwise it is not set
|
||||
void setVoltsPerStep(float voltage, uint32_t steps);
|
||||
float getVoltsPerStep() { return _voltsPerStep; };
|
||||
@ -44,11 +42,32 @@ public:
|
||||
void disable();
|
||||
bool isEnabled() { return _enabled; };
|
||||
|
||||
|
||||
// experimental estimate DUV index
|
||||
// WARNING: USE WITH CARE
|
||||
//
|
||||
// input in mW per cm2 == typical the output of getUV()
|
||||
float estimateDUVindex(float mWcm2);
|
||||
//
|
||||
// https://github.com/RobTillaart/ML8511/issues/4
|
||||
// discusses the calibration
|
||||
// see readme.md how to reverse engineer the factor for
|
||||
// the estimateDUVindex() conversion function.
|
||||
// a value of 1.61 was found to be far more accurate
|
||||
//
|
||||
// returns false if f < 0.01 (to force positive only)
|
||||
bool setDUVfactor(float f);
|
||||
float getDUVfactor() { return _DUVfactor; };
|
||||
|
||||
|
||||
private:
|
||||
uint8_t _analogPin;
|
||||
uint8_t _enablePin;
|
||||
float _voltsPerStep;
|
||||
bool _enabled;
|
||||
|
||||
|
||||
float _DUVfactor;
|
||||
};
|
||||
|
||||
|
||||
|
@ -35,6 +35,9 @@ void setup()
|
||||
|
||||
// manually enable / disable the sensor.
|
||||
light.enable();
|
||||
|
||||
light.setDUVfactor(1.80); // calibrate your sensor
|
||||
|
||||
Serial.print("\tmW cm^2");
|
||||
Serial.print("\tDUV index");
|
||||
Serial.println();
|
||||
|
@ -0,0 +1,75 @@
|
||||
//
|
||||
// FILE: ML8511_determine_DUV_factor.ino
|
||||
// AUTHOR: Rob Tillaart
|
||||
// VERSION: 0.1.0
|
||||
// PURPOSE: demo ML8511 UV sensor - to determine DUV factor
|
||||
// DATE: 2021-06-19
|
||||
// URL: https://github.com/RobTillaart/ML8511
|
||||
|
||||
// BREAKOUT
|
||||
// +-------+--+
|
||||
// VIN |o +-+| mounting hole
|
||||
// 3V3 |o +-+|
|
||||
// GND |o |
|
||||
// OUT |o |
|
||||
// EN |o S | Sensor
|
||||
// +----------+
|
||||
//
|
||||
// EN = ENABLE
|
||||
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <ML8511.h>
|
||||
|
||||
#define ANALOGPIN A0
|
||||
#define ENABLEPIN 7
|
||||
|
||||
ML8511 light(ANALOGPIN, ENABLEPIN);
|
||||
|
||||
// for calculating the average
|
||||
float sum = 0;
|
||||
uint32_t count = 0;
|
||||
|
||||
|
||||
void setup()
|
||||
{
|
||||
Serial.begin(115200);
|
||||
Serial.println(__FILE__);
|
||||
Serial.println("UV UltraViolet ML8511");
|
||||
|
||||
// manually enable / disable the sensor.
|
||||
light.enable();
|
||||
|
||||
Serial.print("\tmW cm^2");
|
||||
Serial.print("\tDUV index");
|
||||
Serial.println();
|
||||
}
|
||||
|
||||
|
||||
void loop()
|
||||
{
|
||||
Serial.print("enter reference DUV:\t");
|
||||
// flush all
|
||||
while (Serial.available() > 0) Serial.read();
|
||||
while (Serial.available() == 0);
|
||||
float DUV = Serial.parseFloat();
|
||||
Serial.println(DUV);
|
||||
|
||||
float UV = light.getUV();
|
||||
Serial.print("UV mW cm^2:\t\t");
|
||||
Serial.println(UV, 4);
|
||||
|
||||
Serial.print("DUV factor:\t\t");
|
||||
float factor = DUV / UV;
|
||||
Serial.println(factor, 2);
|
||||
|
||||
// calculate the average
|
||||
count++;
|
||||
sum += factor;
|
||||
Serial.print("DUV factor average:\t");
|
||||
Serial.println(sum / count);
|
||||
Serial.println();
|
||||
Serial.println();
|
||||
}
|
||||
|
||||
// -- END OF FILE --
|
@ -5,13 +5,18 @@ ML8511 KEYWORD1
|
||||
|
||||
|
||||
# Methods and Functions (KEYWORD2)
|
||||
reset KEYWORD2
|
||||
getUV KEYWORD2
|
||||
setVoltsPerStep KEYWORD2
|
||||
getVoltsPerStep KEYWORD2
|
||||
|
||||
enable KEYWORD2
|
||||
disable KEYWORD2
|
||||
setVoltsPerStep KEYWORD2
|
||||
isEnabled KEYWORD2
|
||||
estimateDUVindex KEYWORD2
|
||||
|
||||
estimateDUVindex KEYWORD2
|
||||
setDUVfactor KEYWORD2
|
||||
getDUVfactor KEYWORD2
|
||||
|
||||
# Instances (KEYWORD2)
|
||||
|
||||
|
@ -13,9 +13,9 @@
|
||||
"repository":
|
||||
{
|
||||
"type": "git",
|
||||
"url": "https://github.com/RobTillaart/ML8511"
|
||||
"url": "https://github.com/RobTillaart/ML8511.git"
|
||||
},
|
||||
"version": "0.1.5",
|
||||
"version": "0.1.6",
|
||||
"license": "MIT",
|
||||
"frameworks": "arduino",
|
||||
"platforms": "*"
|
||||
|
@ -1,5 +1,5 @@
|
||||
name=ML8511
|
||||
version=0.1.5
|
||||
version=0.1.6
|
||||
author=Rob Tillaart <rob.tillaart@gmail.com>
|
||||
maintainer=Rob Tillaart <rob.tillaart@gmail.com>
|
||||
sentence=ML8511 - UV sensor - library for Arduino
|
||||
|
@ -48,18 +48,40 @@ can set those with setVoltagePerStep()
|
||||
```
|
||||
|
||||
It is possible to always enable the sensor by connecting the EN to 3V3.
|
||||
The value of the enablePin in the constructor should then be ommitted
|
||||
The value of the enablePin in the constructor should then be omitted
|
||||
or set to a negative value;
|
||||
|
||||
When connecting to an Arduino UNO one can use the 3V3 of the Arduino to power
|
||||
the sensor. However it is not possible to connect the enablepin directly to the
|
||||
the sensor. However it is not possible to connect the enable pin directly to the
|
||||
sensor. Use a voltage divider (10K + 20K) to convert the 5 Volts to ~3.3 Volts.
|
||||
|
||||
|
||||
## Interface
|
||||
|
||||
- **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.
|
||||
- **float getUV(uint8_t energyMode = HIGH)** returns mW per cm2, energyMode = HIGH or LOW
|
||||
- **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.
|
||||
- **float getVoltsPerStep()** idem
|
||||
- **void enable()** manually enable
|
||||
- **void disable()** manually disable
|
||||
- **bool isEnabled()** get status.
|
||||
|
||||
|
||||
#### experimental
|
||||
|
||||
WARNING: USE WITH CARE
|
||||
|
||||
- **float estimateDUVindex(float mWcm2)** input in mW per cm2, returns a value between 0 and ~15(?)
|
||||
- **void setDUVfactor(float f)** set the conversion factor
|
||||
- **float getDUVfactor()** returns the set conversion factor (default 1.61)
|
||||
|
||||
See below how to determine the DUV factor for your sensor.
|
||||
|
||||
|
||||
## Sensor sensitivity
|
||||
|
||||
Indoors there is very little UV light so use a known UV source like
|
||||
a blacklight or go outside in the sun.
|
||||
a black-light or go outside in the sun.
|
||||
|
||||
The formula to convert the ADC reading to mW cm^2 is based upon the graph
|
||||
shown in the datasheet. As I have no reference source to calibrate the library
|
||||
@ -69,8 +91,26 @@ The sensor has its peak sensitivity ( >80% ) from λ = 300-380 nm
|
||||
with an absolute peak at λ = 365 nm.
|
||||
|
||||
|
||||
## Experimental
|
||||
(use at own risk)
|
||||
## Experimental DUVindex
|
||||
|
||||
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
|
||||
|
||||
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 |
|
||||
|
||||
|
||||
#### 0.1.5 and before
|
||||
|
||||
The formula for the experimental **estimateDUVindex(power)** is based on
|
||||
the following facts / assumptions:
|
||||
@ -82,19 +122,31 @@ This is the most lethal the sensor can sense > 80%.
|
||||
(Erythemal action spectrum)
|
||||
As we cannot differentiate this is the safest choice.
|
||||
|
||||
The DUV index can be used for warning for sunburn etc.
|
||||
Please note that this library is NOT calibrated so **USE AT OWN RISK**
|
||||
|
||||
#### 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.
|
||||
|
||||
|
||||
Table based upon https://en.wikipedia.org/wiki/Ultraviolet_index,
|
||||
#### Calibrate estimateDUVindex()
|
||||
|
||||
| DUV INDEX | Description |
|
||||
|:-----:|:----|
|
||||
| 0 - 2 | LOW |
|
||||
| 3 - 5 | MODERATE |
|
||||
| 6 - 7 | HIGH |
|
||||
| 8 - 10 | VERY HIGH |
|
||||
| 11+ | EXTREME |
|
||||
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. You need to make multiple measurements during the
|
||||
(preferably unclouded) day and calculate the factor.
|
||||
|
||||
```
|
||||
DUV from weather station
|
||||
factor = --------------------------
|
||||
getUV();
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
|
||||
## More about UV
|
||||
@ -103,7 +155,8 @@ https://en.wikipedia.org/wiki/Ultraviolet_index
|
||||
|
||||
|
||||
## Notes
|
||||
|
||||
- 3V3 Sensor so do not connect to 5V directly.
|
||||
- do not forget to connect the EN to either an enablePIN or to 3V3 (constantly enabled).
|
||||
- library does not work with an external ADC
|
||||
- library does not work with an external ADC. (todo?)
|
||||
|
||||
|
@ -29,6 +29,7 @@
|
||||
// assertNAN(arg); // isnan(a)
|
||||
// assertNotNAN(arg); // !isnan(a)
|
||||
|
||||
|
||||
#include <ArduinoUnitTests.h>
|
||||
|
||||
|
||||
@ -46,22 +47,10 @@ unittest_teardown()
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
unittest(test_new_operator)
|
||||
{
|
||||
assertEqualINF(exp(800));
|
||||
assertEqualINF(0.0/0.0);
|
||||
assertEqualINF(42);
|
||||
|
||||
assertEqualNAN(INFINITY - INFINITY);
|
||||
assertEqualNAN(0.0/0.0);
|
||||
assertEqualNAN(42);
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
#define ANALOGPIN 0
|
||||
|
||||
|
||||
unittest(test_constructor)
|
||||
{
|
||||
fprintf(stderr, "VERSION: %s\n", ML8511_LIB_VERSION);
|
||||
@ -72,6 +61,9 @@ unittest(test_constructor)
|
||||
light.setVoltsPerStep(3.3, 4095);
|
||||
assertEqualFloat(3.3/4095, light.getVoltsPerStep(), 0.0001);
|
||||
|
||||
light.reset();
|
||||
assertEqualFloat(5.0/1023, light.getVoltsPerStep(), 0.0001);
|
||||
|
||||
assertTrue(light.isEnabled());
|
||||
light.disable();
|
||||
assertFalse(light.isEnabled());
|
||||
@ -79,22 +71,104 @@ unittest(test_constructor)
|
||||
assertTrue(light.isEnabled());
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
unittest(test_getUV)
|
||||
{
|
||||
ML8511 light(ANALOGPIN);
|
||||
// need god mode to fill the analogRead...
|
||||
// does not work properly
|
||||
|
||||
GodmodeState* state = GODMODE();
|
||||
state->reset();
|
||||
int future[6] = {0, 0, 0, 400, 500, 600};
|
||||
state->analogPin[0].fromArray(future, 6);
|
||||
|
||||
ML8511 light(ANALOGPIN); // no/default enable pin
|
||||
|
||||
assertEqualFloat(0, light.getUV(), 0.0001);
|
||||
assertTrue(light.isEnabled());
|
||||
assertEqualFloat(0, light.getUV(LOW), 0.0001);
|
||||
assertFalse(light.isEnabled());
|
||||
assertEqualFloat(0, light.getUV(HIGH), 0.0001);
|
||||
assertTrue(light.isEnabled());
|
||||
|
||||
assertEqualFloat(0, light.getUV(), 0.0001);
|
||||
assertEqualFloat(0, light.getUV(LOW), 0.0001);
|
||||
assertEqualFloat(0, light.getUV(HIGH), 0.0001);
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
unittest(test_getUV_2)
|
||||
{
|
||||
// need god mode to fill the analogRead...
|
||||
// does not work properly
|
||||
|
||||
GodmodeState* state = GODMODE();
|
||||
state->reset();
|
||||
int future[6] = {0, 0, 0, 0, 0, 0};
|
||||
state->analogPin[0].fromArray(future, 6);
|
||||
|
||||
ML8511 light(ANALOGPIN, 4); // set enable pin
|
||||
|
||||
assertEqualFloat(0, light.getUV(), 0.0001);
|
||||
assertTrue(light.isEnabled());
|
||||
assertEqualFloat(0, light.getUV(LOW), 0.0001);
|
||||
assertFalse(light.isEnabled());
|
||||
assertEqualFloat(0, light.getUV(HIGH), 0.0001);
|
||||
assertTrue(light.isEnabled());
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
unittest(test_setDUVfactor)
|
||||
{
|
||||
ML8511 light(ANALOGPIN);
|
||||
|
||||
light.enable();
|
||||
assertEqualFloat(0, light.getUV(HIGH), 0.0001);
|
||||
|
||||
for (float uv = 0; uv < 1; uv += 0.1)
|
||||
for (float factor = 0.10; factor < 2.01; factor += 0.1)
|
||||
{
|
||||
fprintf(stderr, "%f\t", uv);
|
||||
assertEqualFloat(0, light.estimateDUVindex(0), 0.0001);
|
||||
light.setDUVfactor(factor);
|
||||
assertEqualFloat(factor, light.getDUVfactor(), 0.0001);
|
||||
}
|
||||
|
||||
fprintf(stderr, "\nOUT OF RANGE\n");
|
||||
assertTrue(light.setDUVfactor(0.577));
|
||||
assertEqualFloat(0.577, light.getDUVfactor(), 0.0001);
|
||||
|
||||
assertFalse(light.setDUVfactor(0));
|
||||
assertEqualFloat(0.577, light.getDUVfactor(), 0.0001);
|
||||
|
||||
assertFalse(light.setDUVfactor(-1.0));
|
||||
assertEqualFloat(0.577, light.getDUVfactor(), 0.0001);
|
||||
|
||||
light.reset();
|
||||
assertEqualFloat(1.61, light.getDUVfactor(), 0.0001);
|
||||
}
|
||||
|
||||
|
||||
unittest(test_estimateDUVindex)
|
||||
{
|
||||
ML8511 light(ANALOGPIN);
|
||||
|
||||
light.enable();
|
||||
|
||||
for (float mW = 0; mW < 10; mW += 0.1)
|
||||
{
|
||||
fprintf(stderr, "%f\t", mW);
|
||||
fprintf(stderr, "%f\n", light.estimateDUVindex(mW));
|
||||
assertEqualFloat(1.61 * mW, light.estimateDUVindex(mW), 0.0001);
|
||||
}
|
||||
|
||||
light.setDUVfactor(1.0);
|
||||
for (float mW = 0; mW < 10; mW += 0.1)
|
||||
{
|
||||
fprintf(stderr, "%f\t", mW);
|
||||
fprintf(stderr, "%f\n", light.estimateDUVindex(mW));
|
||||
assertEqualFloat(1.0 * mW, light.estimateDUVindex(mW), 0.0001);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user