//const int led_farben_max = 3;
// Anzahl der LED Farben :=3 für eine RGB-LED
const int led_farben_max = 3;
// Anzahl der LED Farben :=1 für eine einfarbige LED
#include
// Bilbiothek für die ESP8266 Baugruppe und seine WIFI Funktionalität
#include
// Bilbiothek für die I2C Datenkummunikation - hier zum Datenaustausch mit dem Lichtsensor benötigt
#include
// Basis-Bilbiothek für die Adafruit Sensor Biblitheken (Bibliothek von Adafruit)
#include // Bibliothek für den Lichtsensor TSL2661 (Bibliothek von Adafruit)
String sVersion = "Version 2.0";
String sVersion2 = " vom 22.02.2019";
const char* ssid = "PHOTOMETER-";
// der erste Teil des Namens des WIFI Access Points
const char* password = "";
// Auf "" (leerer String) eingestellt für einen offenen Access Point ohne Passwort
unsigned long ulReqcount;
// Zähler für die Aufrufe der Webseite
int ledPin[] = {13, 12, 14};
// grüne LED an GPIO Pin 13/D7, rote LED an GPIO Pin 12/D6, blaue LED an GPIO Pin 14/D5 (hier für Lucky Light: LL-509RGBC2E-006)
const String farbkennung[] = {"Grüne LED  ", "Rote LED  ", "Blaue LED  ", "Grüne, rote und blaue LED"}; // Texte für die RGB-Auswahl Buttons (Grün, Rot, Blau)
const int sdaPin = 4;
// SDA an GPIO/Pin 4 / D2 Anschluss-Pin für das SDA-Signal zur Datenkommunikation mit dem Lichtsensor
const int sclPin = 5;
// SCL an GPIO/Pin 5 / D1 Anschluss-Pin für das SCL-Signal zur Datenkommunikation mit dem Lichtsensor
const int data_runs = 7;
// Anzahl der Wiederholungsmessungen aus denen dann ein Mittelwert gebildet wird
float VIS_IRZEROdata[] = {0.0, 0.0, 0.0};
// Für die aktuellen Nullproben-Messwerte vom Lichtsensor. Hier der Messwert vom Sensorteil, der im VIS und IR Bereich misst
float IRZEROdata[]
= {0.0, 0.0, 0.0};
// Für die aktuellen Nullproben-Messwerte vom Lichtsensor. Hier der Messwert vom Sensorteil, der nur im IR Bereich misst
float LUXZEROdata[]
= {0.0, 0.0, 0.0};
// Für die aktuellen Nullproben-Messwerte vom Lichtsensor. Hier die laut Datenblatt berechnete Beleuchtungsstärke in Lux
float VIS_IRdata[]
= {0.0, 0.0, 0.0};
// Für die aktuellen Proben-Messwerte vom Lichtsensor. Hier der Messwert vom Sensorteil, der im VIS und IR Bereich misst
float IRdata[]
= {0.0, 0.0, 0.0};
// Für die aktuellen Proben-Messwerte vom Lichtsensor. Hier der Messwert vom Sensorteil, der nur im IR Bereich misst
float LUXdata[]
= {0.0, 0.0, 0.0};
// Für die aktuellen Proben-Messwerte vom Lichtsensor. Hier die laut Datenblatt berech- nete Beleuchtungsstärke in Lux
float E_LUX[]
= {0.0, 0.0, 0.0};
// Für die berechnete Extiktionen aus den Beleuchtungsstärke-Daten
float E_VIS_IR[]
= {0.0, 0.0, 0.0};
// Für die berechnete Extiktionen aus den Sensor-Daten vom Sensorteil der im VIS und IR Bereich misst
float E_IR[]
= {0.0, 0.0, 0.0};
// Für die berechnete Extiktionen aus den Sensor-Daten vom Sensorteil der nur im IR Bereich misst
int probenzeilenIndex
= 0;
// Zeiger auf die aktuell zu füllende Probenzeile
const int probenzeilenMax = 5;
// Anzahl der Zeilen für Proben in der HTML Tabelle
float LUX_werte[3][probenzeilenMax];
// Array für Proben-Messwerte(Beleuchtungsstärke-Daten) vom Lichtsensor in Lux
float E_werte[3][probenzeilenMax];
// Array für berechnete Extiktionen aus den Beleuchtungsstärke-Daten
String anzeige
= "a";
// Kennung für die Anzeige der Tabellenanteile a=alle, g=grün, r=rot, b=blau
bool download
= false;
// Flag: Wenn True Download der DatenTabelle gewünscht, wenn False dan nicht.
String datentabelle
= "";
// Nimmt Datentabelle für den Download auf
const String trennzeichen = "\t";
// Spalten Trennzeichen für die Datentabelle (\t := Tabulatorzeichen)
uint16_t broadband = 0;
// Sensor-Daten vom Sensorteil der im VIS und IR Bereich misst
uint16_t infrared = 0;
// Sensor-Daten vom Sensorteil der nur im IR Bereich misst
WiFiServer server(80);
// Eine Instanz auf dem Server für Port 80 erzeugen
Adafruit_TSL2561_Unified tsl = Adafruit_TSL2561_Unified(TSL2561_ADDR_FLOAT, 12345); //Objekt anlegen für den TSL2561 Sensor
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
void displaySensorDetails(void) { // Zeigt einige Basisinformationen über den Sensor an
sensor_t sensor;
tsl.getSensor(&sensor);
Serial.println("------------------------------------");
Serial.print ("Sensor: "); Serial.println(sensor.name);
//Serial.print ("Driver Ver: "); Serial.println(sensor.version);
//Serial.print ("Unique ID: "); Serial.println(sensor.sensor_id);
Serial.print ("Max Value: "); Serial.print(sensor.max_value); Serial.println(" lux");
Serial.print ("Min Value: "); Serial.print(sensor.min_value); Serial.println(" lux");
Serial.print ("Resolution: "); Serial.print(sensor.resolution); Serial.println(" lux");
delay(500);
}
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
void configureSensor(void) { // Verstärkung und Integrationszeit konfigurieren
// tsl.setGain(TSL2561_GAIN_1X);
// 1-fache Verstärkung bei hoher Beleuchtungsstärke, um eine Sensor-Übersättigung zu verhindern
// tsl.setGain(TSL2561_GAIN_16X);
// 16-fache Verstärkung bei niediger Beleuchtungsstärke, um die Sensor-Empfindlichkeit zu erhöhen
tsl.enableAutoRange(true);
// Automatische Verstärkung ... Verstärkung wechselt automatisch zwischen 1-fach und 16-fach
// Das Ändern der Integrationszeit bewirkt eine Änderung der Sensor-Genauigkeit bzw. seiner Auflösung (402ms = 16-bit data)
tsl.setIntegrationTime(TSL2561_INTEGRATIONTIME_13MS);
/* 13ms: Schnell aber niedrige Auflösung */
//tsl.setIntegrationTime(TSL2561_INTEGRATIONTIME_101MS);
/* 101ms: Mittlere Geschwindigkeit und Auflösung */
//tsl.setIntegrationTime(TSL2561_INTEGRATIONTIME_402MS);
/* 402ms: Langsamste Geschwindigkeit aber hohe Auflösung (16-bit
Daten)*/
// Einstellungen zur Info ausgeben
Serial.print ("Gain: "); Serial.println("Auto");
Serial.print ("Timing: "); Serial.println("13 ms");
//Serial.print ("Timing: "); Serial.println("101 ms");
Serial.println("------------------------------------");
}
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
bool readSensor(int color) { // Sensor-Messdaten auslesen
sensors_event_t event;
// Ein neues TSL2561 Lichtsensor Ereigniss einrichten
int ok = 0;
// Zähler für geglückte Messungen
float LUX[data_runs + 1];
// Daten Array zur Aufname der Einzelmessungen (ein Element mehr als Summenspeicher)
float VIS_IR[data_runs + 1]; // Daten Array zur Aufname der Einzelmessungen
float IR[data_runs + 1];
// Daten Array zur Aufname der Einzelmessungen
float LUX_max = 0.0, LUX_min = 0.0;
for (int j = 0; j <= data_runs; j++) { // Daten Arrays mit "0.0"-Werten vorbelegen
LUX[j]
= 0.0;
VIS_IR[j] = 0.0;
IR[j]
= 0.0;
}
if (led_farben_max > 1) {
digitalWrite(ledPin[color], HIGH); // LED einschalten
}
else {
digitalWrite(ledPin[1], HIGH); // LED einschalten
}
for (int j = 0; j <= data_runs - 1; j++) { // Messungen "data_runs"-mal wiederholen und Einzelmessungen in Daten Array eintragen
tsl.getEvent(&event);
// Eine Messung durchführen
if (event.light) {
// Wird nur dann "Wahr" wenn erfolgreich gemesen wurde
LUX[j] = (event.light * 1.0);
// LUX-Wert abholen
if (j == 0) {
LUX_max = LUX[j]; // Min- und Max-Werte am Anfang auf den ersten Messwert setzten
LUX_min = LUX[j];
}
if (LUX[j] > LUX_max) LUX_max = LUX[j];
// Neuen Max-Wert suchen und ggf neu setzten
if (LUX[j] < LUX_min) LUX_min = LUX[j];
// Neuen Min-Wert suchen und ggf neu setzten
tsl.getLuminosity (&broadband, &infrared);
// VIS-IR- und IR-Werte auslesen
VIS_IR[j] = (broadband * 1.0);
// VIS-IR-Wert zuweisen
IR[j]
= (infrared * 1.0);
// IR-Wert zuweisen
delay(25);
// Zwischen den Messungen warten (Zeit in Milli-Sekunden)
ok += 1;
// Zähler für geglückte Messungen um 1 erhöhen
}
else { // Wenn "event.light = 0 lux" ist, dann ist der Sensor möglicher Weise auch gesättigt und es können keinen Daten generiert
Serial.println("Der Sensor-Messwert ist unbrauchbar. -> Sensor fehler!");
}
}
if (led_farben_max > 1) {
digitalWrite(ledPin[color], LOW); // LED wieder ausschalten
}
else {
digitalWrite(ledPin[1], LOW); // LED wieder ausschalten
}
if (ok >= data_runs) {
// Nur wenn alle Einzelmessungen erfolgreich durchgeführt wurden ...
for (int j = 0; j <= data_runs - 1; j++) { // Einzelmessungen aufaddieren
LUX[data_runs]
+= LUX[j];
VIS_IR[data_runs] += VIS_IR[j];
IR[data_runs]
+= IR[j];
}
// Die aufaddierte Summe der Einzelwerte ohne den Max-Wert und den Min-Wert geteilt durch Anzahl der Einzelmessungen minus 2 ergibt den bereinigten Mittelwert
LUX[data_runs]
= (LUX[data_runs] - LUX_max - LUX_min)
/ (data_runs * 1.0 - 2.0);
// Die aufaddierte Summe der Einzelwerte geteilt durch Anzahl der Einzelmessungen ergibt den jeweiligen Mittelwert
VIS_IR[data_runs] = VIS_IR[data_runs] / (data_runs * 1.0);
IR[data_runs]
= IR[data_runs]
/ (data_runs * 1.0);
LUXdata[color]
= LUX[data_runs];
// Berechneten LUX-Wert in die Datentabelle übertragen
VIS_IRdata[color] = VIS_IR[data_runs]; // Berechneten VIS-IR-Wert in die Datentabelle übertragen
IRdata[color]
= IR[data_runs];
// Berechneten IR-Wert in die Datentabelle übertragen
return true;
}
else {
return false;
}
}
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
void cleardata() {
Serial.println("Cleardata(RESET) aufgerufen!" );
for (int zeilennummer = 0; zeilennummer < probenzeilenMax; zeilennummer++) { // Datentabelle mit "0.0"-Werten vorbelegen
for (int ledfarbe = 0; ledfarbe < led_farben_max; ledfarbe++) {
LUX_werte[ledfarbe][zeilennummer] = 0.0;
E_werte[ledfarbe][zeilennummer]
= 0.0;
LUXZEROdata[ledfarbe]
= 0.0;
VIS_IRZEROdata[ledfarbe]
= 0.0;
IRZEROdata[ledfarbe]
= 0.0;
}
}
probenzeilenIndex = 0;
}
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
void setup() {
// Globale Voreinstellungen
for (int led = 0; led < led_farben_max; led++) {
if (led_farben_max == 1) pinMode(ledPin[1], OUTPUT); // Wenn nur eine LED an GPIO Pin 12/D6 (für einfarbige LED), den Anschluss für die LED als Ausgang konfigurieren
if (led_farben_max > 1) pinMode(ledPin[led], OUTPUT);
// Die Anschlüsse für die LEDs als Ausgänge konfigurieren
}
ulReqcount = 0;
// Seitenaufrufszähler auf 0 setzten
Serial.begin(9600);
// Serielle Verbindung initialisieren
WiFi.mode(WIFI_AP);
// WIFI Access Point Modus initialisieren
if (led_farben_max > 1) {
for (int led = 0; led < led_farben_max; led++) {
digitalWrite(ledPin[led], HIGH); // LED einschalten
delay(2000);
// 2000 ms warten
digitalWrite(ledPin[led], LOW); // LED ausschalten
}
}
else {
digitalWrite(ledPin[1], HIGH); // LED einschalten
delay(6000);
// 6000 ms warten
digitalWrite(ledPin[1], LOW); // LED ausschalten
}
for (int zeilennummer = 0; zeilennummer < probenzeilenMax; zeilennummer++) { // Arrays mit "0.0"-Werten vorbelegen
for (int ledfarbe = 0; ledfarbe < led_farben_max; ledfarbe++) {
LUX_werte[ledfarbe][zeilennummer] = 0.0;
E_werte[ledfarbe][zeilennummer]
= 0.0;
}
}
Serial.println("");
Serial.println(String(sVersion + String (led_farben_max) + sVersion2));
// Das Anhängsel für den Access Point Namen (SSID) aus der MAC ID des ESP Moduls generieren.
// Um den Namen nicht zu lang werden zu lassen, werden hier nur die letzten 2 Bytes verwendet.
// Der Aufwand dient dazu, um einen möglichst einmaligen Access Point Namen für das jeweils verwendete ESP Modul entstehten zu lassen.
uint8_t mac[WL_MAC_ADDR_LENGTH];
WiFi.softAPmacAddress(mac);
String macID = String((mac[WL_MAC_ADDR_LENGTH - 2] * 256 + mac[WL_MAC_ADDR_LENGTH - 1]), DEC);
macID.toUpperCase();
String AP_NameString = ssid + macID;
char AP_NameChar[AP_NameString.length() + 1];
memset(AP_NameChar, 0, AP_NameString.length() + 1);
for (int i = 0; i < AP_NameString.length(); i++) AP_NameChar[i] = AP_NameString.charAt(i);
// WIFI Access Point und den Web Server starten
WiFi.softAP(AP_NameChar, password);
server.begin();
Serial.print("WIFI Access Point gestartet. Name (SSID) : "); Serial.println(AP_NameChar);
Serial.print("WEB-Server erreichbar unter der IP-Adresse): "); Serial.println(WiFi.softAPIP());
Serial.println("");
// Lichtsensor TSL2561
Wire.pins(sdaPin, sclPin); // Wire.pins(int sda, int scl) // Anschlusspins für das I2C-Protokoll zuordnen
if (!tsl.begin()) {
// Sensor initialisieren und im Fehlerfall eine Meldung ausgeben
Serial.print("Ooops, es konnte kein TSL2561-Sensor erkannt werden ...! (Hardware Fehler) Programm Abbruch!!! Reset nötig!!!");
while (1);
}
displaySensorDetails();
// Zeigt einige Basisinformationen über den Sensor an
configureSensor();
// Verstärkung und Integrationszeit konfigurieren
Serial.println("");
}
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
void loop() {
WiFiClient client = server.available(); // Prüfen ob ein WIFI-Client verbunden ist
if (!client) return;
// Wenn keiner verbunden ist, wieder zum Anfang. Also warten bis der WIFI-Client Daten
Serial.println("Neuer WIFI-Client");
// Jetzt ist ein Client verbunden ...
unsigned long ultimeout = millis() + 250;
while (!client.available() && (millis() < ultimeout)) delay(1);
if (millis() > ultimeout) {
Serial.println("WIFI-Client Fehler: Connection-Time-Out!");
return;
}
String sRequest = client.readStringUntil('\r'); // Die Erste Zeile der Anfrage lesen
client.flush();
if (sRequest == "") { // WIFI-Client stoppen, wenn die Anfrage leer ist
Serial.println("WIFI-Client Fehler: Leere Anfrage! -> WIFI-Client angehalten");
client.stop();
return;
}
// "get path"; Das Ende des Pfads ist entweder ein " " oder ein "?" (Syntax z.B. GET /?pin=SENSOR_A HTTP/1.1)
String sPath = "", sParam = "", sCmd = "";
String sGetstart = "GET ";
int iStart, iEndSpace, iEndQuest;
iStart = sRequest.indexOf(sGetstart);
if (iStart >= 0) {
iStart += +sGetstart.length();
iEndSpace = sRequest.indexOf(" ", iStart);
iEndQuest = sRequest.indexOf("?", iStart);
// Den Pfad und die Parameter isolieren
if (iEndSpace > 0) {
if (iEndQuest > 0) { // Pfad und Parameter
sPath = sRequest.substring(iStart, iEndQuest);
sParam = sRequest.substring(iEndQuest, iEndSpace);
}
else { // Pfad aber keine Parameter
sPath = sRequest.substring(iStart, iEndSpace);
}
}
}
// Den Befehl isolieren
if (sParam.length() > 0) {
int iEqu = sParam.indexOf("=");
if (iEqu >= 0) {
sCmd = sParam.substring(iEqu + 1, sParam.length());
Serial.println(sCmd);
}
}
// Die HTML Seite erzeugen
String sResponse, sHeader;
String sResponseStart = "";
String sResponseTab
= "";
if (sPath != "/") { // Für unpassenden Pfad eine 404-Fehlerseite generieren
sResponse = "404 Not Found
Not Found
Die angeforderte Webseite (URL) gibt es auf diesem Server nicht. Bitte zurück zu 192.168.4.1!
";
sHeader = "HTTP/1.1 404 Not found\r\n";
sHeader += "Content-Length: ";
sHeader += sResponse.length();
sHeader += "\r\n Content-Type: text/html\r\n Connection: close\r\n\r\n";
}
else { // Für passenden Pfad ...
if (sCmd.length() > 0) { // Auf die Parameter reagieren ...
Serial.print("Command: "); Serial.println(sCmd);
if (sCmd.indexOf("READZERO") >= 0) {
for (int color = 0; color < led_farben_max; color++) {
if (!readSensor(color)) Serial.println("Sensor Fehler"); // Messung durchführen und Sensordaten auslesen - ggf. Fehler melden
else {
LUXZEROdata[color]
= LUXdata[color];
VIS_IRZEROdata[color] = VIS_IRdata[color];
IRZEROdata[color]
= IRdata[color];
}
}
}
if (sCmd.indexOf("READTSL") >= 0) { // Wenn Button "Probe" gedrückt, ...
for (int color = 0; color < led_farben_max; color++) {
if (!readSensor(color)) Serial.println("Sensor Fehler"); // Messung durchführen und Sensordaten auslesen - ggf. Fehler melden
LUX_werte[color][probenzeilenIndex] = LUXdata[color];
// Die Extinktionen berechnen
if (LUXdata[color] > 0.0 & LUXZEROdata[color] > 0.0) {
E_LUX[color] = -log10((LUXdata[color] * 1.0) / (LUXZEROdata[color] * 1.0));
}
else {
E_LUX[color] = 0.0;
}
if (VIS_IRdata[color] > 0.0 & VIS_IRZEROdata[color] > 0.0) {
E_VIS_IR[color] = -log10((VIS_IRdata[color] * 1.0) / (VIS_IRZEROdata[color] * 1.0));
}
else {
E_VIS_IR[color] = 0.0;
}
if (IRdata[color] > 0.0 & IRZEROdata[color] > 0.0) {
E_IR[color] = -log10((IRdata[color] * 1.0) / (IRZEROdata[color] * 1.0));
}
else {
E_IR[color] = 0.0;
}
}
probenzeilenIndex += 1;
if (probenzeilenIndex >= probenzeilenMax) probenzeilenIndex = 0;
}
if (sCmd.indexOf("CLEARDATA") >= 0) {
cleardata()
; // Wenn Button "Reset" gedrückt, ...
}
if (led_farben_max > 1) {
if (sCmd.indexOf("GR")
>= 0) {
anzeige = "g"
; // Wenn Button "grün" gedrückt, ...
}
if (sCmd.indexOf("RT")
>= 0) {
anzeige = "r"
; // Wenn Button "rot" gedrückt, ...
}
if (sCmd.indexOf("BL")
>= 0) {
anzeige = "b"
; // Wenn Button "blau" gedrückt, ...
}
if (sCmd.indexOf("ALL")
>= 0) {
anzeige = "a"
; // Wenn Button "alle" gedrückt, ...
}
}
if (sCmd.indexOf("DOWNLOAD") >= 0) {
download = true; // Wenn Button "Download" gedrückt, ...
}
}
// Die Extinktion mit ggf neuer Leerprobe erneut berechnen
for (int color = 0; color < led_farben_max; color++) {
for (int zeilennummer = 0; zeilennummer < probenzeilenMax; zeilennummer++) {
if (LUX_werte[color][zeilennummer] > 0.0 & LUXZEROdata[color] > 0.0) {
E_werte[color][zeilennummer] = -log10((LUX_werte[color][zeilennummer] * 1.0) / (LUXZEROdata[color] * 1.0));
}
else {
E_werte[color][zeilennummer] = 0.0;
}
}
}
sResponseStart = "Photometer";
sResponseStart += ""; // Hintergrundfarbe
sResponseStart += "";
sResponseStart += "