// Arduino db(A) interface für DFLD // dB (A)rduino by DFLD & Laszlo Lebrun // Open Source Lizens GNU GPL // Version für http://www.wikilaerm.de/Preiswerter%20Ersatz%20fuer%20den%20Modulbus-Luxus-Version.pdf oder http://www.wikilaerm.de/Preiswerter%20Ersatz%20fuer%20den%20Modulbus-Miniversion.pdf // Umbau einer TAE-Dose zum DoIt Yourself Ersatz des AK-Modulbus A/D Umformer (2 Kanal-Version mit 0ptionale 7 Segment-Anzeige) // Version 5.0 mit Selbstkalibrierung und Korrekturfaktor für 7 Segment Display //============ (Bibliotheken holen) ============ //#include //Parameter für 7 Seg Display #include #define CLK 6 #define DIO 5 TM1637Display display(CLK, DIO); // Arduino db(A) interface für DFLD //============ (Sensorparameter) ============ int sensorRead ; int sensorMax = 2500; //mV bei 100dB Bei dem Messmodul "Lärm" von AK ist der oberen Bereich 2,5 V int sensorMin = 500; ////mV bei 0dB Bei dem Messmodul "Lärm" von AK ist der unteren Bereich 1V bei 25dB int sensorRise = 200; // mV (100mV = ca. 3dB) per Sek Hier kann die maximale Steigegeschwindigkeit für die Glättung (zum Beispiel um Vogelgezwitscher aus zu filtern) eingestellt werden int sensorFall = 200; // mV (100mV = ca. 3dB) per Sek Hier wird die minimale Abklangeschwindigkeit für die Glättung eingestellt. (100mV/Sek Ist für Fluglärm bei 1000m Höhe optimiert) const long internalReferenceVoltage = 1140L; // Hier kann für eine höhere Genauigkeit die Spannung des Arduinos bei analogReference (INTERNAL) exakt eingegeben werden. int corrFact1 = -100; // Hier kann ein Korrekturwert eingegeben werden, um die Messungen anzupassen (zum Beispiel bei Lärm-Reflexionen, Hindernisse, Alterung des Mikrofons...) 0=keine Korrektur; 20=Anhebung um 1dB; -20=Absenkung um 1dB % // Maximum: +/- 200! // Vorsicht: bei zweiKanalMax = true darf corrFact2 nicht von corrFact1 abweichen! int corrFact2 = 0; // Hier kann ein Korrekturwert eingegeben werden, um die Messungen anzupassen (zum Beispiel bei Lärm-Reflexionen, Hindernisse, Alterung des Mikrofons...) 0=keine Korrektur; 20=Anhebung um 1dB; -20=Absenkung um 1dB % //============ (Optionen)============ byte value1 = 0; //pin für Wert 1: Normalerweise 0, (beim LCD Display mit 6 Taster ist 0 belegt, dann 1) byte value2 = 1; //pin für Wert 2: Normalerweise 1, (beim LCD Display mit 6 Taster ist 0 belegt, dann 2) boolean zweiKanal = false ; // Vorbelegung für Modus 2 Messwerte A&B boolean zweiKanalMax = false; // Wenn das 2. Kanal das Maximum der erste Messung übertragen soll boolean debugSerial = true; // Werte nicht zum Computer, sondern am Serial Monitor ausgeben boolean ledDisplay = false; // Wenn 7 Segment Display vorhanden ist boolean lcdDisplay = false; // Wenn 2zeiliger LCD Display vorhanden ist // ============ (Variablen) ============ int corrMax1; // Speichert die Obergrenze für die Glättung int corrMin1; // Speichert die Untergrenze für die Glättung int corrMax2; // Speichert die Obergrenze für die Glättung int corrMin2; // Speichert die Untergrenze für die Glättung unsigned int battVolts; // Speichert die selbst ermittelte Batteriespannung int A0milliVolts; // Speichert die 1. Messung in Millivolt int A1milliVolts; // Speichert die 2. Messung in Millivolt unsigned int A0Summe; //Summe Kanal1 (kann bis 25*2500 = 62500 gehen) unsigned int A1Summe; //Summe Kanal2 (kann bis 25*2500 = 62500 gehen) int A0memory = 150; // Zum zwischenspeichern von Werte für die Glättung int A1memory = 150; // Zum zwischenspeichern von Werte für die Glättung int A1Max; //Maximumsberechnung A0 -> Kanal2 int A0dB; // Umrechnung in dB für Kanal1 int A1dB; // Umrechnung in dB für Kanal2 byte A0Byte; // Umrechnung nach AK Modulbus Konvention für Kanal1 byte A1Byte; // Umrechnung nach AK Modulbus Konvention für Kanal1 byte inbyte; // Eingang von Serielle Schnittstelle byte mode = 1; // Arbeitsmodus im AK Modulbus Format byte i; byte val; // allgemeine Variable für Zähler, Können mehrmals belegt werden boolean halt = false; //Keine dB Daten senden // ============ (Initialisierung) ============ void setup(void) { Serial.begin(9600); // Die serielle Schnittstelle wird auf 9600 Baud eingestellt delay(100); // Alles mit der Ruhe! Wir gönnen uns eine kleine Pause corrMax1 = sensorMax - corrFact1; //Wir berechnen die Grenzen Gemäß Korrekturfaktor um corrMin1 = sensorMin - corrFact1; //Wir berechnen die Grenzen Gemäß Korrekturfaktor um corrMax2 = sensorMax - corrFact2; //Wir berechnen die Grenzen Gemäß Korrekturfaktor um corrMin2 = sensorMin - corrFact2; //Wir berechnen die Grenzen Gemäß Korrekturfaktor um if (debugSerial) { Serial.print("dB (A)rduino by DFLD & Laszlo Lebrun License GNU GPL "); // Ein bisschen Werbung muss sein! Serial.println( "\r\n" ); Serial.print(" 70dB vom Sensor ergeben nach Korrektur "); Serial.print(" " ); Serial.print(" dB am Display und zur Zellensoftware)"); Serial.println( "\r\n\r\n" ); } if (zweiKanal) { mode = 2; } delay(100); // Alles mit der Ruhe! Wir gönnen uns eine kleine Pause analogReference(DEFAULT); // Der A/D Wandler wird auf 0-5 V eingestellt (eigentlich überflüssig, aber: doppelt genäht hält besser) display.setBrightness(0x08); //Beim 7 Segmentdisplay auf halber Helligkeit, wir wollen die Bude doch nicht beleuchten! // Turn off all segments. // byte data[] = {0x0, 0x0, 0x0, 0x0}; // display.setSegments(data); // Turn on all segments. // byte data[] = {0xff, 0xff, 0xff, 0xff}; // display.setSegments(data); if (zweiKanal) { // Im Zweikanalmodus das 7 Segmentdisplay Initialisieren } else { // Im ein Kanalmodus die 2 ersten Ziffern löschen, dann "db" auf die 2 letzten schreiben byte data[] = {0x0, 0x0, 0b01011110 , 0b01111100}; display.setSegments(data); } delay(100); // Selbskalibrierung // Berechnung der eigene Stromversorgungsspannung (Wichtig für den A/D Umwandler) for (int i = 0; i <= 3; i++) battVolts = getBandgap(); //4 readings seem required for stable value? delay(100); } //============ (Laufendes Programm)============ void loop(void) { // Für die Berechnung arbeiten wir mit 16 bit, es werden 25 Werte alle 40 mS aufaddiert, // dann gefiltert, zuletzt wird das Mittelwert mit einer div/25 berechnet. // so wird die schnelle und sparsame 16Bit Festpunktarithmetik am besten ausgenutzt. A0Summe = 0; A1Summe = 0; A1Max = 0; A1milliVolts = 0; for (i = 0; i < 24; i++) { ; // Zuerst noch den Handshake von AK Modulbus abbilden // Liegt ein Befehl von der serielle Schnittstelle vor? val = Serial.available(); if (val > 0) { inbyte = Serial.read(); //abholen } //Modusumschaltung switch (inbyte) { case 0: break; case 1: delay(10); Serial.write (inbyte) ; // Umschaltung nach Mode 1 inbyte = 0 ; mode = 1; zweiKanal = false; halt = false; break; case 2: delay(10); Serial.write (inbyte) ; // Umschaltung nach Mode 2 inbyte = 0 ; mode = 2; zweiKanal = true; halt = false; break; case 27: delay(10); Serial.write (inbyte) ; // Abfrage von serielle Schnittstelle inbyte = 0 ; halt = true; break; case 11: inbyte = 0 ; delay(10); Serial.write (mode) ; halt = false; // Im Modus 11 wird die Anfrage der Software beantwortet und das Modus wird zurück gegeben. break; } // Wertglättung des Analogeingangs innerhalb der Sekunde: 25 Werte werden addiert. A0milliVolts = map(analogRead(value1), 0, 1023, 0, battVolts); A0milliVolts = constrain (A0milliVolts, (A0memory - sensorFall), (A0memory + sensorRise)); A0Summe = A0Summe + A0milliVolts; if (zweiKanal) { if (zweiKanalMax) { A1Max = max (A0milliVolts, A1Max); // Das Maximum innerhalb 1 s wird ermittelt. } else { A1milliVolts = map(analogRead(value2), 0, 1023, 0, battVolts); A1milliVolts = constrain (A1milliVolts, (A1memory - sensorFall), (A1memory + sensorRise)); A1Summe = A1Summe + A1milliVolts; } } delay(40); // Jetzt haben wir gut gearbeitet und warten 40 ms auf dem nächsten Takt } // Endlich ist die Schleife fertig, jetzt können wir die Ergebnisse verarbeiten // Die Mittelwerte und ggf. das Maximum werdengebildet A0milliVolts = A0Summe / 25; A1milliVolts = A1Summe / 25; A1milliVolts = max (A1milliVolts, A1Max); // A1 mittelwert oder A0Max A0memory = A0milliVolts; // Die Werte werden für das nächste mal zurückgesetzt. A1memory = A1milliVolts; // Umrechnungen // Das map() Befehl berechnet ein Eingangsbereich in einem Ausgangsbereich... genial, nicht wahr? // Danach muss noch mit constrain() eine Begrenzung erfolgen, um Fehlern beim Überlauf abzufangen. // WIR bauen kein Day0 Schlupfloch für die NSA ein ;-) A0dB = map(A0milliVolts, corrMin1, corrMax1, 0, 1050); // in dB x10) A0dB = constrain (A0dB, 0, 1000); A1dB = map(A1milliVolts, corrMin2, corrMax2, 0, 1050); // in dB x10) A1dB = constrain (A1dB, 0, 1000); A0Byte = map(A0milliVolts, corrMin1, corrMax1, 50, 260); // ein Bytewert aller halbe dB) A0Byte = constrain (A0Byte, 50, 250); A1Byte = map(A1milliVolts, corrMin2, corrMax2, 50, 260); // ein Bytewert aller halbe dB) A1Byte = constrain (A1Byte, 50, 250); if (debugSerial) //Sendet die Werte zum Serial Monitor; { Serial.println(A0dB / 10); } else //Sendet die Werte zur Zellensoftware; { if (!halt) { switch (mode) { case 0: break; case 1: Serial.write(A0Byte); // Genau das erwartet die Zellensoftware: Ein einziges klitzekleines Byte jede Sekunde, Dafür habe man den ganzen Schmarrn gemacht? break; case 2: Serial.write(1); Serial.write(A0Byte); Serial.write(2); Serial.write(A1Byte); // im 2 Kanalmodus kommen sogar 4 Bytes break; default: break; //weitere Modi nicht implementiert } } } if (mode < 3) { display.showNumberDec(A0dB / 10, false, 2, 0); // Jetzt schreiben wir noch die Messung(en) auf den 7 Segment Display if (zweiKanal) { display.showNumberDec(A1dB / 10, false, 2, 2); } } else { display.showNumberDec(mode, false, 2, 0); //falls nicht in Modus 1 oder 2, wird das Modus am Display angezeigt. Insbesondere // wenn das display permanent 27db anzeigt, hat das Handshake nicht funktioniert. // wenn das display permanent 11db anzeigt, wird auf eine Modusantwort gewartet. } } // ============ (Funktionsbaustein(e)) ============ // Hier wird Voodoo-Hexerei betrieben, das hab ich aus dem Internet heraus geholt, sowas könnte ich nie selber schreiben. // Function created to obtain chip's actual Vcc voltage value, using internal bandgap reference // This provides ability to maintain A/D calibration with changing Vcc // For 328 chip only, mod needed for 1280/2560 chip int getBandgap(void) { // REFS1 REFS0 --> 0 1, AVcc internal ref. // MUX3 MUX2 MUX1 MUX0 --> 1110 1.1V (VBG) ADMUX = (0 << REFS1) | (1 << REFS0) | (0 << ADLAR) | (1 << MUX3) | (1 << MUX2) | (1 << MUX1) | (0 << MUX0); // Start a conversion ADCSRA |= _BV( ADSC ); // Wait for it to complete while ( ( (ADCSRA & (1 << ADSC)) != 0 ) ); // Scale the value unsigned int results = (((internalReferenceVoltage * 1024L) / ADC) + 5L) ; return results; } // im Februar 2016 (Schaffen wir das?... Wir haben es geschafft!) // Laszlo Lebrun für DFLD.