Hier (https://stoppi-homemade-physics.de/pirani-drucksensor/) habe ich bereits meinen Bau eines einfachen Pirani-Drucksensors auf Basis einer geöffneten, kleinen Glühbirne präsentiert. Das Problem damit ist die notwendige Kalibierung mit einem zweiten Sensor.
Den Piranisensor Thyracont VRP habe ich mir nun gebraucht über ebay-kleinanzeigen für 40 Euro gekauft. Genauer gesagt nur den Pirani-Kopf. Die Berechnung des Drucks anhand der Ausgangsspannung des Sensors (Ausgabebereich 0.5 – 10V) und die Darstellung am Display übernimmt ein Arduino Nano.
Der Arduino besitzt bereits mehrere integrierte ADC (Analog-Digital-Konverter), jedoch bei 10 bit nur mit einer Auflösung von ca. 5 mV. Damit ich aber die Ausgabespannung des Sensors genauer bestimmen kann, verwende ich einen 16 bit ADC und zwar den ADS1115.
Diesen stelle ich im Arduino-Programm auf eine Auflösung von 0.125 mV/bit ein. Damit können Spannungen im Bereich +/–4.096 V erfasst werden. Nun liefert der Piranisensor vom Typ THYRACONT VRP aber Spannungen im Bereich [0.5V, 10V]. Daher benötige ich noch einen simplen Spannungsteiler bestehend aus 2 Widerständen in Serie oder einem Spindeltrimmer. Diesen Trimmer stelle ich z.B. so fix ein, dass eine Eingangsspannung von 10.0 V eine Ausgangsspannung von 4.0 V ergibt. Damit habe ich den Messbereich des ADS1115 schön ausgenutzt. Natürlich muss ich dann die gemessene Spannung noch mit diesem Umrechnungsfaktor 10.0/4.0 multiplizieren.
Jetzt kenne ich einmal die Ausgangsspannung des Piranisensors. Nun muss ich nur noch den Zusammenhang Ausgangsspannung U – Druck p kennen. Leider ist dieser Zusammenhang nur in Form einer Tabelle (siehe oben) vorhanden und nicht bereits als Formel. Das macht aber nichts.
Da der Druckbereich sehr groß ist und zwar von 0.1 Pa bis 100000 Pa (= 1 bar), logarithmiere ich zunächst die Drücke. Dann liegen die Werte nur noch im Bereich [–1, 5]. Jetzt kenne ich die sog. Stützstellen [U, lg(p)]. Der Sensor liefert mir je nach Druck eine beliebige Spannung zwischen 0.5V und 10V. Jetzt muss ich die Stützstellen nur noch geschickt interpolieren und schon habe ich meinen lg(p)-Wert, aus dem ich dann durch Bildung der 10-er Potenz den Druck p berechnen kann. Ich könnte die Stützstellen zum Beispiel linear, also mittels Geradenstücken interpolieren. Das wird aber meistens nicht sehr genau, da der Graph zwischen den Stützstellen in der Regel gekrümmt ist.
Genau dafür gibt es die sog. Spline-Interpolation bzw. die sog. kubischen Splines. Man muss 2-dimensional nur die Stützstellen (x,y) kennen und werfe ich dann einen beliebigen x-Wert in den Algorithmus, wirft mir dieser den interpolierten y-Wert aus. Im konkreten Fall gebe ich dem Rechner die Sensorspannung U und ich erhalte den lg(p) bzw. in weiterer Folge den Druck p.
In der obigen Abbildung sieht man schön den kurvigen Graphen lg(p) in Abhängigkeit von der Spannung U. Weiters erkennt man, dass die interpolierten, orangen Punkte sich sehr schön zwischen die blauen Stützstellen schmiegen. Genau was wir wollten. Für den Arduino gibt es dafür sogar eine eigene Library und zwar InterpolationLib.h.
Den Spindeltrimmer verstellt man so lange, bis die Eingangsspannung (siehe Multimeter) mit der vom Arduino gemessenen Spannung (siehe Bildschirm) übereinstimmt:
Leider habe ich den Piranisensor Thyracont VRP noch immer nicht aus Deutschland erhalten und der Verkäufer meldet sich auch nicht mehr. Mmmmmh, scheinbar wieder Lehrgeld bezahlt 🙁
Arduino-Code für Piranisensor Thyracont VRP:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 |
#include <LiquidCrystal_I2C.h> #include <Wire.h> #include <Adafruit_ADS1015.h> #include "InterpolationLib.h" float voltage; float voltage_correction; float lgp, p; int i; Adafruit_ADS1115 ads1115; // Construct an ads1115 at the default address: 0x48 int16_t adc0, adc1, adc2, adc3; const int numValues = 22; double xValues[22] = {0.53, 0.56, 0.61, 0.69, 0.76, 0.98, 1.33, 1.75, 2.09, 2.95, 4.05, 5.1, 5.79, 7.09, 8.17, 8.8, 9.11, 9.51, 9.71, 9.81, 9.86, 10.0}; double yValues[22] = {-1.0, -0.69897, -0.39794, -0.154902, 0, 0.30103, 0.60206, 0.845098, 1, 1.30103, 1.60206, 1.845098, 2, 2.30103, 2.60206, 2.845098, 3, 3.30103, 3.60206, 3.845098, 4, 5}; LiquidCrystal_I2C lcd(0x27,16,2); // set the LCD address to 0x27 for a 16 chars and 2 line display. ACHTUNG: Adresse kann auch 0x3F sein !!! // Anschlüsse: // GND - GND // VCC - 5V // SDA - ANALOG Pin 4 // SCL - ANALOG pin 5 // =========================== // ======= SETUP ========= // =========================== void setup() { while (!Serial) { ; } Serial.begin(115200); lcd.begin(); // initialize the lcd lcd.backlight(); lcd.setCursor(0,0); lcd.print("Pirani-"); lcd.setCursor(0,1); lcd.print("Manometer"); delay(4000); lcd.setCursor(0,0); lcd.print(" "); lcd.setCursor(0,1); lcd.print(" "); //ads1015.begin(); // Initialize ads1015 ads1115.begin(); // Initialize ads1115 // 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 // ------- ------- // ads1115.setGain(GAIN_TWOTHIRDS); // 2/3x gain +/- 6.144V 1 bit = 3 mV 0.1875 mV (default) // ads1115.setGain(GAIN_ONE); // 1x gain +/- 4.096V 1 bit = 2 mV 0.125 mV // ads1115.setGain(GAIN_TWO); // 2x gain +/- 2.048V 1 bit = 1 mV 0.0625 mV // ads1115.setGain(GAIN_FOUR); // 4x gain +/- 1.024V 1 bit = 0.5 mV 0.03125 mV // ads1115.setGain(GAIN_EIGHT); // 8x gain +/- 0.512V 1 bit = 0.25 mV 0.015625 mV // ads1115.setGain(GAIN_SIXTEEN); // 16x gain +/- 0.256V 1 bit = 0.125 mV 0.0078125 mV ads1115.setGain(GAIN_ONE); // 1x gain +/- 4.096V 1 bit = 2 mV 0.125 mV voltage_correction = 10.0/4.0; } // =========================== // ======= LOOP ========= // =========================== void loop() { voltage = 0.0; for(i = 1; i <= 10; i++) { voltage = voltage + ads1115.readADC_SingleEnded(0); delay(50); } voltage = voltage / 10.0; // Mittelwert /* adc1 = ads1115.readADC_SingleEnded(1); adc2 = ads1115.readADC_SingleEnded(2); adc3 = ads1115.readADC_SingleEnded(3); */ voltage = voltage * 0.125; // vom ADS1115 eingelesene Spannung in mV (!) nach dem Spannungsteiler voltage = voltage * voltage_correction / 1000.0; // tatsächlich vom Piranisensor ausgegebene Spannung in Volt (!) vor dem Spannungsteiler /* Serial.print(Interpolation::Step(xValues, yValues, numValues, voltage, 0.0)); Serial.print(','); Serial.print(Interpolation::Step(xValues, yValues, numValues, voltage, 0.5)); Serial.print(','); Serial.print(Interpolation::Step(xValues, yValues, numValues, voltage, 1.0)); Serial.print(','); Serial.print(Interpolation::SmoothStep(xValues, yValues, numValues, voltage)); Serial.print(','); Serial.print(Interpolation::Linear(xValues, yValues, numValues, voltage, false)); Serial.print(','); Serial.print(Interpolation::Linear(xValues, yValues, numValues, voltage, true)); Serial.print(','); Serial.println(Interpolation::ConstrainedSpline(xValues, yValues, numValues, xValue),5); */ Serial.print(" U = "); Serial.print(voltage, 3); lgp = Interpolation::CatmullSpline(xValues, yValues, numValues, voltage); Serial.print(" : lg(p) = "); Serial.print(lgp,5); p = pow(10, lgp); Serial.print(" , p = "); Serial.println(p,4); lcd.setCursor(0,0); lcd.print("p = "); lcd.print(p,1); lcd.print(" Pa "); lcd.setCursor(0,1); lcd.print("p = "); lcd.print(p/100.0,1); lcd.print(" mbar "); delay(500); } |
Ich habe mir noch einen weiteren Piranisensor über ebay-kleinanzeigen gekauft und zwar einen Pfeiffer/Inficon TPR265. Für diesen Sensor gibt es sogar bereits eine einfache Beziehung p = p(U) und zwar p(U) = 10^ (U – 3.5) in Pascal. Dadurch entfällt die kompliziertere Splineinterpolation wie beim obigen Thyracont VRP – Sensor.
Während ich auf die Pirani-Sensoren warte, sind einige Vakuumteile (KF-16 Flanschteile) aus China eingetroffen.
So, der Inficon-Piranisensor ist wohlbehalten bei mir angekommen. Um ihn dann testen zu können, muss ich mir wohl oder übel eine Vakuumpumpe kaufen. Jetzt geht es einmal ans verkabeln. 6 und 8 mm T-Stücke bekam ich aus aus Litauen mit der Post…
Leider stellte sich heraus, dass der Piranisensor TPR265 defekt ist. Am Ausgang war bei Atmosphärendruck eine Spannung < 0.5 V zu messen. Dies deutet auf einen irreparablen Fadenbruch hin. Deshalb musste ich mich nach einem Ersatz umsehen und wurde ebenfalls auf ebay-kleinanzeigen fündig. Dieses Mal sollte es der Nachfolger vom TPR265, der Inficon TPR280 sein. Die Anschlussbelegung und Auswertung ist bei beiden ident, sprich pro Druckdekade ändert sich die Ausgangsspannung um 1V. Messbereich ist wieder 1000 mbar bis 0.001 mbar.
Hoffentlich kommt der Sensor heil bei mir an. Inzwischen konnte ich das Gehäuse meines Messgeräts fertigstellen:
Der Inficon TPR280-Sensor ist gestern bei mir angekommen und er scheint zu funktionieren. Er besitzt im Gegensatz zum TPR265 kein Potentiometer zum kalibieren, sondern nur einen Taster. Betätige ich diesen bei Atmosphärendruck, so zeigt er wie zu erwarten war 1000 mbar an. Jedoch driftet dann dieser Wert relativ schnell davon zum Beispiel in Richtung 1100 mbar, obwohl der Druck unverändert ist. Keine Ahnung ob dies normal ist. Laut Datenblatt besitzt er bei 100-1000 mbar nur eine Genauigkeit von 50% des angezeigten Werts. Von daher darf ich mir für solch hohe Drücke nicht wirklich viel vom Sensor erwarten. Wichtig ist für mich dann ohnedies der Druckbereich zwischen 0.1 und 100 Pa.
Zum Testen habe ich im Moment nur eine einfache 12V-Luftpumpe. Damit erziele ich einen Enddruck von lediglich 500 mbar.
Damit ich meine gekauften/selbstgebastelten Pirani-Sensoren testen kann, habe ich mir nun eine neue 2-stufige Drehschieberpumpe gegönnt. Sie ist zwar nicht gerade stark, aber durch die zwei Stufen dürfte hoffentlich das erzielbare Vakuum nicht so schlecht sein. Ich habe mich bewusst für die schwächere Version entschieden, da erstens meine Wohnung schon überquillt wegen meiner vielen Experimente und ich deshalb etwas kompaktes benötige und zweitens meine Rezipienten wirklich nur ein geringes Volumen aufweisen. Meist sind es nur kleine Zylindergläser, die evakuiert werden müssen…
Die kleine, kompakte Vakuumpumpe ist heute bei mir angekommen und scheint ohne Probleme zu funktionieren. Leider besitzt sie wie die meisten Pumpen einen 1/4″ SAE Anschluss am Ansaugstutzen. Für dieses Gewinde ist es gar nicht einfach einen entsprechenden Adapter zu finden.
Bei einem polnischen Händler bin ich bzgl. Adapter fündig geworden: https://vacuumchambers.eu/en_US/p/Fitting-14-SAE-Flare-to-barb-for-6mm-hose/20
Daher konnte ich am Wochenende den ersten Testlauf mit der Vakuumpumpe starten. Ich komme nach ca. 5 min Laufzeit auf ein Vakuum von rund 4 Pa. Da geht aber vermutlich noch ein bisschen nach unten. Der Piranisensor und das Arduino-Programm laufen aber scheinbar ohne Probleme…
Hier noch das Youtube-Video:
Arduino-Code für Piranisensor Pfeiffer/Inficon TPR280:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 |
#include <LiquidCrystal_I2C.h> #include <Wire.h> #include <Adafruit_ADS1015.h> #include "InterpolationLib.h" float voltage; float voltage_correction; float p; int i; Adafruit_ADS1115 ads1115; // Construct an ads1115 at the default address: 0x48 int16_t adc0, adc1, adc2, adc3; LiquidCrystal_I2C lcd(0x27,16,2); // set the LCD address to 0x27 for a 16 chars and 2 line display. ACHTUNG: Adresse kann auch 0x3F sein !!! // Anschlüsse: // GND - GND // VCC - 5V // SDA - ANALOG Pin 4 // SCL - ANALOG pin 5 // =========================== // ======= SETUP ========= // =========================== void setup() { while (!Serial) { ; } Serial.begin(115200); lcd.begin(); // initialize the lcd lcd.backlight(); lcd.setCursor(0,0); lcd.print("Pirani-"); lcd.setCursor(0,1); lcd.print("Manometer"); delay(4000); lcd.setCursor(0,0); lcd.print(" "); lcd.setCursor(0,1); lcd.print(" "); //ads1015.begin(); // Initialize ads1015 ads1115.begin(); // Initialize ads1115 // 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 // ------- ------- // ads1115.setGain(GAIN_TWOTHIRDS); // 2/3x gain +/- 6.144V 1 bit = 3 mV 0.1875 mV (default) // ads1115.setGain(GAIN_ONE); // 1x gain +/- 4.096V 1 bit = 2 mV 0.125 mV // ads1115.setGain(GAIN_TWO); // 2x gain +/- 2.048V 1 bit = 1 mV 0.0625 mV // ads1115.setGain(GAIN_FOUR); // 4x gain +/- 1.024V 1 bit = 0.5 mV 0.03125 mV // ads1115.setGain(GAIN_EIGHT); // 8x gain +/- 0.512V 1 bit = 0.25 mV 0.015625 mV // ads1115.setGain(GAIN_SIXTEEN); // 16x gain +/- 0.256V 1 bit = 0.125 mV 0.0078125 mV ads1115.setGain(GAIN_ONE); // 1x gain +/- 4.096V 1 bit = 2 mV 0.125 mV voltage_correction = 10.0/4.0; } // =========================== // ======= LOOP ========= // =========================== void loop() { voltage = 0.0; for(i = 1; i <= 10; i++) { voltage = voltage + ads1115.readADC_SingleEnded(0); delay(50); } voltage = voltage / 10.0; // Mittelwert /* adc1 = ads1115.readADC_SingleEnded(1); adc2 = ads1115.readADC_SingleEnded(2); adc3 = ads1115.readADC_SingleEnded(3); */ voltage = voltage * 0.125; // vom ADS1115 eingelesene Spannung in mV (!) nach dem Spannungsteiler voltage = voltage * voltage_correction / 1000.0; // tatsächlich vom Piranisensor ausgegebene Spannung in Volt (!) vor dem Spannungsteiler Serial.print(" U = "); Serial.print(voltage, 3); p = pow(10, voltage - 3.5); Serial.print(" , p = "); Serial.print(p,2); Serial.println(" Pa"); lcd.setCursor(0,0); lcd.print("p = "); lcd.print(p,2); lcd.print(" Pa "); lcd.setCursor(0,1); lcd.print("p = "); lcd.print(p/100.0,2); lcd.print(" mbar "); delay(500); } |