Ultraviolet Intensity Measurement Experiment: Unterschied zwischen den Versionen
Dl8rds (Diskussion | Beiträge) (→Solution Concept) |
Dl8rds (Diskussion | Beiträge) (→Component List) |
||
| Zeile 44: | Zeile 44: | ||
Total: 369 € | Total: 369 € | ||
| + | |||
| + | == Code == | ||
| + | |||
| + | <pre> | ||
| + | #include <SPI.h> | ||
| + | #include <Ethernet.h> | ||
| + | #include <Wire.h> | ||
| + | #include <Adafruit_ADS1015.h> | ||
| + | #include "cactus_io_BME280_I2C.h" | ||
| + | |||
| + | Adafruit_ADS1115 ads; /* Use this for the 16-bit version */ | ||
| + | |||
| + | // Create the BME280 object | ||
| + | BME280_I2C bme; // I2C using default 0x77 | ||
| + | // or BME280_I2C bme(0x76); // I2C using address 0x76 | ||
| + | |||
| + | // Enter a MAC address and IP address for your controller below. | ||
| + | // The IP address will be dependent on your local network: | ||
| + | byte mac[] = { | ||
| + | 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED | ||
| + | }; | ||
| + | IPAddress ip(192, 168, 178, 177); | ||
| + | |||
| + | // Initialize the Ethernet server library | ||
| + | // with the IP address and port you want to use | ||
| + | // (port 80 is default for HTTP): | ||
| + | EthernetServer server(80); | ||
| + | |||
| + | void setup() { | ||
| + | // Open serial communications and wait for port to open: | ||
| + | Serial.begin(9600); | ||
| + | while (!Serial) { | ||
| + | ; // wait for serial port to connect. Needed for native USB port only | ||
| + | } | ||
| + | |||
| + | |||
| + | // start the Ethernet connection and the server: | ||
| + | Ethernet.begin(mac, ip); | ||
| + | server.begin(); | ||
| + | Serial.print("server is at "); | ||
| + | Serial.println(Ethernet.localIP()); | ||
| + | |||
| + | // The ADC input range (or gain) can be changed via the following | ||
| + | // functions, but be careful never to exceed VDD +0.3V max, or to | ||
| + | // exceed the upper and lower limits if you adjust the input range! | ||
| + | // Setting these values incorrectly may destroy your ADC! | ||
| + | // ADS1015 ADS1115 | ||
| + | // ------- ------- | ||
| + | // ads.setGain(GAIN_TWOTHIRDS); // 2/3x gain +/- 6.144V 1 bit = 3mV 0.1875mV (default) | ||
| + | ads.setGain(GAIN_ONE); // 1x gain +/- 4.096V 1 bit = 2mV 0.125mV | ||
| + | // ads.setGain(GAIN_TWO); // 2x gain +/- 2.048V 1 bit = 1mV 0.0625mV | ||
| + | // ads.setGain(GAIN_FOUR); // 4x gain +/- 1.024V 1 bit = 0.5mV 0.03125mV | ||
| + | // ads.setGain(GAIN_EIGHT); // 8x gain +/- 0.512V 1 bit = 0.25mV 0.015625mV | ||
| + | // ads.setGain(GAIN_SIXTEEN); // 16x gain +/- 0.256V 1 bit = 0.125mV 0.0078125mV | ||
| + | |||
| + | ads.begin(); | ||
| + | |||
| + | if (!bme.begin()) { | ||
| + | Serial.println("Could not find a valid BME280 sensor, check wiring!"); | ||
| + | while (1); | ||
| + | } | ||
| + | |||
| + | bme.setTempCal(-1); | ||
| + | } | ||
| + | |||
| + | |||
| + | //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 loop() { | ||
| + | // listen for incoming clients | ||
| + | EthernetClient client = server.available(); | ||
| + | if (client) { | ||
| + | Serial.println("new client"); | ||
| + | // an http request ends with a blank line | ||
| + | boolean currentLineIsBlank = true; | ||
| + | while (client.connected()) { | ||
| + | if (client.available()) { | ||
| + | char c = client.read(); | ||
| + | Serial.write(c); | ||
| + | // if you've gotten to the end of the line (received a newline | ||
| + | // character) and the line is blank, the http request has ended, | ||
| + | // so you can send a reply | ||
| + | if (c == '\n' && currentLineIsBlank) { | ||
| + | // send a standard http response header | ||
| + | client.println("HTTP/1.1 200 OK"); | ||
| + | client.println("Content-Type: text/html"); | ||
| + | client.println("Connection: close"); // the connection will be closed after completion of the response | ||
| + | client.println("Refresh: 5"); // refresh the page automatically every 5 sec | ||
| + | client.println(); | ||
| + | client.println("<!DOCTYPE HTML>"); | ||
| + | client.println("<html>"); | ||
| + | |||
| + | client.println("<table>"); | ||
| + | |||
| + | client.print("<tr><td>"); | ||
| + | client.print("Light Intensity:"); | ||
| + | client.print("</td><td>"); | ||
| + | int sensorReading = analogRead(0); | ||
| + | Serial.print("Light Intensity: "); Serial.println(sensorReading); | ||
| + | client.print(sensorReading); | ||
| + | client.println("</td></tr>"); | ||
| + | |||
| + | int16_t adc0, adc1, adc2, adc3; | ||
| + | float adc0_1, adc1_1, adc2_1, adc3_1; | ||
| + | float uvIntensity_0, uvIntensity_1, uvIntensity_2, uvIntensity_3; | ||
| + | |||
| + | client.print("<tr><td>"); | ||
| + | client.print("UVB1 (mW/cm2):"); | ||
| + | client.print("</td><td>"); | ||
| + | adc0 = ads.readADC_SingleEnded(0); | ||
| + | adc0_1 = (float)adc0 / 8000; | ||
| + | uvIntensity_0 = mapfloat(adc0_1, 0.987, 2.8, 0.0, 15.0); | ||
| + | Serial.print("UVB1: "); Serial.println(uvIntensity_0); | ||
| + | client.print(uvIntensity_0); | ||
| + | client.println("</td></tr>"); | ||
| + | |||
| + | client.print("<tr><td>"); | ||
| + | client.print("UVB2 (mW/cm2):"); | ||
| + | client.print("</td><td>"); | ||
| + | adc1 = ads.readADC_SingleEnded(1); | ||
| + | adc1_1 = (float)adc1 / 8000; | ||
| + | uvIntensity_1 = mapfloat(adc1_1, 0.999, 2.8, 0.0, 15.0); | ||
| + | Serial.print("UVB2: "); Serial.println(uvIntensity_1); | ||
| + | client.print(uvIntensity_1); | ||
| + | client.println("</td></tr>"); | ||
| + | |||
| + | client.print("<tr><td>"); | ||
| + | client.print("UV1 (mW/cm2):"); | ||
| + | client.print("</td><td>"); | ||
| + | adc2 = ads.readADC_SingleEnded(2); | ||
| + | adc2_1 = (float)adc2 / 8000; | ||
| + | uvIntensity_2 = mapfloat(adc2_1, 0.989, 2.8, 0.0, 15.0); | ||
| + | Serial.print("UV1: "); Serial.println(uvIntensity_2); | ||
| + | client.print(uvIntensity_2); | ||
| + | client.println("</td></tr>"); | ||
| + | |||
| + | client.print("<tr><td>"); | ||
| + | client.print("UV2 (mW/cm2):"); | ||
| + | client.print("</td><td>"); | ||
| + | adc3 = ads.readADC_SingleEnded(3); | ||
| + | adc3_1 = (float)adc3 / 8000; | ||
| + | uvIntensity_3 = mapfloat(adc3_1, 0.999, 2.8, 0.0, 15.0); | ||
| + | Serial.print("UV2: "); Serial.println(uvIntensity_3); | ||
| + | client.print(uvIntensity_3); | ||
| + | client.println("</td></tr>"); | ||
| + | |||
| + | bme.readSensor(); | ||
| + | |||
| + | client.print("<tr><td>"); | ||
| + | client.print("Pressure (millibar)"); | ||
| + | client.print("</td><td>"); | ||
| + | Serial.print("Pressure (millibar): "); Serial.println(bme.getPressure_MB()); | ||
| + | client.print(bme.getPressure_MB()); | ||
| + | client.println("</td></tr>"); | ||
| + | |||
| + | client.print("<tr><td>"); | ||
| + | client.print("Humidity (%rel)"); | ||
| + | client.print("</td><td>"); | ||
| + | Serial.print("Humidity (%rel): "); Serial.println(bme.getHumidity()); | ||
| + | client.print(bme.getHumidity()); | ||
| + | client.println("</td></tr>"); | ||
| + | |||
| + | client.print("<tr><td>"); | ||
| + | client.print("Temperature (C)"); | ||
| + | client.print("</td><td>"); | ||
| + | Serial.print("Temperature (C): "); Serial.println(bme.getTemperature_C()); | ||
| + | client.print(bme.getTemperature_C()); | ||
| + | client.println("</td></tr>"); | ||
| + | |||
| + | client.println("</table>"); | ||
| + | |||
| + | client.println("</html>"); | ||
| + | break; | ||
| + | } | ||
| + | if (c == '\n') { | ||
| + | // you're starting a new line | ||
| + | currentLineIsBlank = true; | ||
| + | } else if (c != '\r') { | ||
| + | // you've gotten a character on the current line | ||
| + | currentLineIsBlank = false; | ||
| + | } | ||
| + | } | ||
| + | } | ||
| + | // give the web browser time to receive the data | ||
| + | delay(1); | ||
| + | // close the connection: | ||
| + | client.stop(); | ||
| + | Serial.println("client disconnected"); | ||
| + | } | ||
| + | } | ||
| + | </pre> | ||
| + | |||
| + | Sample Output: | ||
| + | |||
| + | <pre> | ||
| + | Light Intensity: 285 | ||
| + | UVB1 (mW/cm2): 0.02 | ||
| + | UVB2 (mW/cm2): 0.02 | ||
| + | UV1 (mW/cm2): 0.02 | ||
| + | UV2 (mW/cm2): 0.02 | ||
| + | Pressure (millibar) 957.97 | ||
| + | Humidity (%rel) 58.39 | ||
| + | Temperature (C) 27.26 | ||
| + | </pre> | ||
Version vom 31. Juli 2017, 21:44 Uhr
Inhaltsverzeichnis
1 Project Scope
When my daughter (my first child) was still a baby, my father bought a UV sensor so he could measure the UV intensity. Babys are very sensitive to UV light and the worst thing he could image was a baby sunburn.
So I thought, what about creating an alarm sensor that would measure UV intensity continuously. The result is this experiment, years later but no less outdated.
2 Solution Concept
I decided to build up the entire solution on the basis of a PoE 803.af Arduino Ethernet, including measurement of environment data and ambient light, including a solar cell based voltage verification.
There are pairs of ML8511 sensors, one pair simply behind the SiO (Silicium Oxide / Quarz glass), and another pair behind a lowpass UVB filter so that these two can measure exclusively UV-A. The UVB intensity is then the difference between the tutal power in this spectral region minus the UVA.
Here is a nice overview of sunburn effects:
The sensor will display all measured values including a normalization on a web page. I am also interested in other environmental data (is the sensor doing fine?), so I decided to use a BME280 sensor also.
3 Component List
- Hammond 1550E aluminium diecast case: 50 €
- 2sided base PCB: 2 €
- Arduino Ethernet: 35 €
- Programmer: 3 €
- Watterott BME280: 14 €
- 4x Sparkfun ML8511: 4x 15€ = 60 €
- 1x Sparkfun TEMT6000: 5 €
- https://www.sparkfun.com/products/8688
- https://learn.sparkfun.com/tutorials/temt6000-ambient-light-sensor-hookup-guide
- Adafruit ADS1115: 15 €
- 2 solar cells: 5 €
- screws, mounts: 10 €
- Edmund Optics UVB lowpass: 80 €
- Cut-Off Position λc (nm): 309±6
- https://www.edmundoptics.com/optics/optical-filters/longpass-edge-filters/n-wg-305-50.8mm-sq.-longpass-filter/
- Quarz glass to pass all wavelengths: 65 €
- Silicon glue: 10 €
- TP-Link PoE injector: 25
Total: 369 €
4 Code
#include <SPI.h>
#include <Ethernet.h>
#include <Wire.h>
#include <Adafruit_ADS1015.h>
#include "cactus_io_BME280_I2C.h"
Adafruit_ADS1115 ads; /* Use this for the 16-bit version */
// Create the BME280 object
BME280_I2C bme; // I2C using default 0x77
// or BME280_I2C bme(0x76); // I2C using address 0x76
// Enter a MAC address and IP address for your controller below.
// The IP address will be dependent on your local network:
byte mac[] = {
0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED
};
IPAddress ip(192, 168, 178, 177);
// Initialize the Ethernet server library
// with the IP address and port you want to use
// (port 80 is default for HTTP):
EthernetServer server(80);
void setup() {
// Open serial communications and wait for port to open:
Serial.begin(9600);
while (!Serial) {
; // wait for serial port to connect. Needed for native USB port only
}
// start the Ethernet connection and the server:
Ethernet.begin(mac, ip);
server.begin();
Serial.print("server is at ");
Serial.println(Ethernet.localIP());
// The ADC input range (or gain) can be changed via the following
// functions, but be careful never to exceed VDD +0.3V max, or to
// exceed the upper and lower limits if you adjust the input range!
// Setting these values incorrectly may destroy your ADC!
// ADS1015 ADS1115
// ------- -------
// ads.setGain(GAIN_TWOTHIRDS); // 2/3x gain +/- 6.144V 1 bit = 3mV 0.1875mV (default)
ads.setGain(GAIN_ONE); // 1x gain +/- 4.096V 1 bit = 2mV 0.125mV
// ads.setGain(GAIN_TWO); // 2x gain +/- 2.048V 1 bit = 1mV 0.0625mV
// ads.setGain(GAIN_FOUR); // 4x gain +/- 1.024V 1 bit = 0.5mV 0.03125mV
// ads.setGain(GAIN_EIGHT); // 8x gain +/- 0.512V 1 bit = 0.25mV 0.015625mV
// ads.setGain(GAIN_SIXTEEN); // 16x gain +/- 0.256V 1 bit = 0.125mV 0.0078125mV
ads.begin();
if (!bme.begin()) {
Serial.println("Could not find a valid BME280 sensor, check wiring!");
while (1);
}
bme.setTempCal(-1);
}
//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 loop() {
// listen for incoming clients
EthernetClient client = server.available();
if (client) {
Serial.println("new client");
// an http request ends with a blank line
boolean currentLineIsBlank = true;
while (client.connected()) {
if (client.available()) {
char c = client.read();
Serial.write(c);
// if you've gotten to the end of the line (received a newline
// character) and the line is blank, the http request has ended,
// so you can send a reply
if (c == '\n' && currentLineIsBlank) {
// send a standard http response header
client.println("HTTP/1.1 200 OK");
client.println("Content-Type: text/html");
client.println("Connection: close"); // the connection will be closed after completion of the response
client.println("Refresh: 5"); // refresh the page automatically every 5 sec
client.println();
client.println("<!DOCTYPE HTML>");
client.println("<html>");
client.println("<table>");
client.print("<tr><td>");
client.print("Light Intensity:");
client.print("</td><td>");
int sensorReading = analogRead(0);
Serial.print("Light Intensity: "); Serial.println(sensorReading);
client.print(sensorReading);
client.println("</td></tr>");
int16_t adc0, adc1, adc2, adc3;
float adc0_1, adc1_1, adc2_1, adc3_1;
float uvIntensity_0, uvIntensity_1, uvIntensity_2, uvIntensity_3;
client.print("<tr><td>");
client.print("UVB1 (mW/cm2):");
client.print("</td><td>");
adc0 = ads.readADC_SingleEnded(0);
adc0_1 = (float)adc0 / 8000;
uvIntensity_0 = mapfloat(adc0_1, 0.987, 2.8, 0.0, 15.0);
Serial.print("UVB1: "); Serial.println(uvIntensity_0);
client.print(uvIntensity_0);
client.println("</td></tr>");
client.print("<tr><td>");
client.print("UVB2 (mW/cm2):");
client.print("</td><td>");
adc1 = ads.readADC_SingleEnded(1);
adc1_1 = (float)adc1 / 8000;
uvIntensity_1 = mapfloat(adc1_1, 0.999, 2.8, 0.0, 15.0);
Serial.print("UVB2: "); Serial.println(uvIntensity_1);
client.print(uvIntensity_1);
client.println("</td></tr>");
client.print("<tr><td>");
client.print("UV1 (mW/cm2):");
client.print("</td><td>");
adc2 = ads.readADC_SingleEnded(2);
adc2_1 = (float)adc2 / 8000;
uvIntensity_2 = mapfloat(adc2_1, 0.989, 2.8, 0.0, 15.0);
Serial.print("UV1: "); Serial.println(uvIntensity_2);
client.print(uvIntensity_2);
client.println("</td></tr>");
client.print("<tr><td>");
client.print("UV2 (mW/cm2):");
client.print("</td><td>");
adc3 = ads.readADC_SingleEnded(3);
adc3_1 = (float)adc3 / 8000;
uvIntensity_3 = mapfloat(adc3_1, 0.999, 2.8, 0.0, 15.0);
Serial.print("UV2: "); Serial.println(uvIntensity_3);
client.print(uvIntensity_3);
client.println("</td></tr>");
bme.readSensor();
client.print("<tr><td>");
client.print("Pressure (millibar)");
client.print("</td><td>");
Serial.print("Pressure (millibar): "); Serial.println(bme.getPressure_MB());
client.print(bme.getPressure_MB());
client.println("</td></tr>");
client.print("<tr><td>");
client.print("Humidity (%rel)");
client.print("</td><td>");
Serial.print("Humidity (%rel): "); Serial.println(bme.getHumidity());
client.print(bme.getHumidity());
client.println("</td></tr>");
client.print("<tr><td>");
client.print("Temperature (C)");
client.print("</td><td>");
Serial.print("Temperature (C): "); Serial.println(bme.getTemperature_C());
client.print(bme.getTemperature_C());
client.println("</td></tr>");
client.println("</table>");
client.println("</html>");
break;
}
if (c == '\n') {
// you're starting a new line
currentLineIsBlank = true;
} else if (c != '\r') {
// you've gotten a character on the current line
currentLineIsBlank = false;
}
}
}
// give the web browser time to receive the data
delay(1);
// close the connection:
client.stop();
Serial.println("client disconnected");
}
}
Sample Output:
Light Intensity: 285 UVB1 (mW/cm2): 0.02 UVB2 (mW/cm2): 0.02 UV1 (mW/cm2): 0.02 UV2 (mW/cm2): 0.02 Pressure (millibar) 957.97 Humidity (%rel) 58.39 Temperature (C) 27.26