Bremsflüssigkeitstester
Warning: WP_Syntax::substituteToken(): Argument #1 ($match) must be passed by reference, value given in /home/www/nicomania_de/wp-content/plugins/wp-syntax/wp-syntax.php on line 380
Neulich hatte ich mal so einen Bremsflüssigkeitstester in Stiftform in der Hand. Als alter Auto-Selberschrauber und Werkzeug-Fetischist dachte ich mir gleich, sowas will ich auch haben. Aber weil das Teil so simpel ist, wollte ich es dann gleich mal selber bauen. Es besteht nur aus einem kleinen Mikrocontroller, der an zwei Elektroden den Widerstand und damit den Wasseranteil in der Bremsflüssigkeit bestimmt. Das sollte doch kein Problem sein.
Um die geeichten Warnschwellen für die Signalisierungen grün/orange/rot nicht neu zu erfinden, habe ich diese an dem Tester eines namhaften Herstellers ausgemessen. Dazu habe ich mit einem Drehpotenziometer an den Elektroden durch Erhöhung und Erniedrigung des Widerstandes und mehrfaches Rumprobieren die Werte ermittelt, bei denen die LEDs umschalten. Also knallhartes Reverse Engineering 😉
Wasseranteil | Widerstand | Spannung | ADC-Wert | |
grün | < 1,5% | > 220kΩ | > 3,82V | > 783 |
orange | 1,5 – 3,0% | 43 – 220kΩ | 1,94 – 3,82V | 398 – 783 |
rot | > 3,0% | < 43kΩ | < 1,94V | < 398 |
Diese Widerstandswerte habe ich anschließend für eine 10Bit ADC Messung bei einer (zunächst angenommenen) VCC Referenz von 5V berechnet und daraus die ADC Werte für ein Programm abgeleitet. Die Formeln dazu stehen im Datenblatt des von mir verwendeten ATtiny13A. Der Widerstand wird über einen Spannungsteiler mit 68kOhm Referenzwiderstand gemessen.
Als Batterie habe ich einen Li-Po 14500 Akku genommen, den man wunderbar mit einer Batteriehalterung einer AA-Zelle verwenden kann. Danach hat sich dann auch maßgeblich das PCB Layout gerichtet. Auf der Rückseite der Leiterplatte dominiert die Batteriehalterung, auf der Vorderseite sind die Bauteile an der Oberfläche verlötet. Das ist zwar stellenweise z.B. am IC Sockel schwierig zu löten, aber so kollidiert es nicht mit dem Akku. Die Messspitze verjüngt sich, sodass man gut in alle Bremsflüssigkeitsbehälter kommen sollte. Wichtig für einen reproduzierbaren Widerstandswert der Flüssigkeit ist es auch, dass die Messspitzen in Fläche und Abstand denen des originalen Messgerätes entsprechen. Dies sind zwei Kreise mit einem Durchmesser von 2,5mm und einem Abstand von 2,5mm. Da ich meine Leiterplatten meistens selber ätze, habe ich die Zuleitungen zu diesen „Testpads“ anstelle von Lötstoplack mit ein bisschen Klarlack isoliert (siehe Foto). Um mit möglichst wenig Bauteilen auszukommen habe ich nicht drei einzelne LEDs verbaut wie beim Original, sonder habe auf eine RGB-LED zurück gegriffen. Dabei entspricht die Farbe Blau dem Orange.
Das Programm ist so gestaltet, dass der ATtiny ständig schläft und durch einen Tasten-Interrupt aufwacht, die Messung ausführt und dann das Ergebnis kurz anzeigt. Dann geht er augenblicklich wieder schlafen und verbraucht währenddessen nur 0,3µA Strom. Tatsächlich werden allerdings 5 Messungen in Folge zu einem Durchschnittsergebnis verrechnet, um einen genaueren Messwert zu erhalten.
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 | #define F_CPU 1200000UL //9,6/8 = 1,2 MHz #include <stdio.h> #include <stdlib.h> //Miscellaneous routines #include <avr/io.h> //Includes other I/O-Headers #include <util/delay.h> #include <avr/interrupt.h> //Interrupt handling routines #include <inttypes.h> //Defines for different int data types #include <avr/sleep.h> #define SCHWELLE_1_5 783 //Schwelle für 1,5% Wasseranteil #define SCHWELLE_3_0 398 //Schwelle für 3,0% Wasseranteil #define ANZAHL_MESSUNGEN 5 //Anzahl der Messungen für Durchschnittswert #define AD_KANAL 3 //Kanal für AD Wandlung #define LEUCHTEN 2000 //Leuchtdauer für LED in ms void led(unsigned char farbe, uint16_t zeit) { if (farbe == 'R') { PORTB &= ~(1<<PB3); // low for (zeit=zeit/10; zeit>0; zeit--) _delay_ms(10); PORTB |= (1<<PB3); // high } else if (farbe == 'G') { PORTB &= ~(1<<PB2); // low for (zeit=zeit/10; zeit>0; zeit--) _delay_ms(10); PORTB |= (1<<PB2); // high } else if (farbe == 'B') { PORTB &= ~(1<<PB1); // low for (zeit=zeit/10; zeit>0; zeit--) _delay_ms(10); PORTB |= (1<<PB1); // high } } uint16_t adc(uint8_t pin) { uint32_t ADC_Summe = 0; uint8_t i = 0; ADCSRA |= (1<<ADEN); //ADC aktivieren ADMUX = (ADMUX & ~(0x1F)) | (pin & 0x1F); //Pin wählen ADCSRA |= (1<<ADSC); //Dummy-Messung starten while (ADCSRA & (1<<ADSC)) ; //auf Abschluss der Messung warten (void)ADCW; //Ergebnis verwerfen while(i < ANZAHL_MESSUNGEN+1) { ADCSRA |= (1<<ADSC); //Messung starten while (ADCSRA & (1<<ADSC)) ; //auf Abschluss der Messung warten ADC_Summe += ADCW; //Ergebnis aufsummieren i++; } ADCSRA &= (0<<ADEN); //ADC deaktivieren return (ADC_Summe/ANZAHL_MESSUNGEN); //Ergebnis zurückgeben } int main(void) { //543210 DDRB = 0b001110; //Ausgang=1, Eingang=0 PORTB = 0b001111; //preset Ausgänge / pullup Eingänge (unbenutzt: pullup ein!) //PCINT0 einstellen GIMSK |= (1<<PCIE); //PCINT enable PCMSK |= (1<<PCINT0); //PCINT Maske //ADC einstellen ADMUX &= ~(1<<REFS0); //AVCC als Referenz (0=AVCC, 1=1.1V) ADCSRA |= (1<<ADPS1) | (1<<ADPS0); //ADC prescaler auf 8 (ADclk mit 150khz) sei(); //enable global Interrupts while(1) { set_sleep_mode(SLEEP_MODE_PWR_DOWN); // Schlafmodus wählen sleep_mode(); // schlafen schicken // <-- hier wird wieder aufgewacht } return 0; } ISR(PCINT0_vect) { if (!(PINB & (1<<PB0))) //wenn fallende Flanke { //Output Test led('R', 100); led('G', 100); led('B', 100); _delay_ms(100); //Messung starten uint16_t messung = adc(AD_KANAL); //Messung auswerten if (messung >= SCHWELLE_1_5) { led('G', LEUCHTEN); } else if ((messung < SCHWELLE_1_5) && (messung > SCHWELLE_3_0)) { led('B', LEUCHTEN); } else if (messung <= SCHWELLE_3_0) { led('R', LEUCHTEN); } //PCI Flag loeschen GIFR |= (1<<PCIF); } } |
Download Programmcode und PCB Layout
Noch keine Kommentare