Mit dem Modul HX711 (erhältlich um 2-3 Euro auf ebay) lässt sich in Kombination mit einem Biegebalken (engl. load cell) und dem Arduino sehr einfach eine Digitalwaage umsetzen. Bei Verwendung eines 100g-Biegebalkens liegt die Messgenauigkeit bei 0.01g. Möchte man die Messrate erhöhen, so muss man Pin 15 des HX711 mit Vcc verbinden. Dann beträgt diese 80 Hz. Eine Tara-Funktion habe ich mit einem Taster umgesetzt. Betätigt man diesen, so wird der aktuelle Messwert als offset eingelesen. Dies führt dazu, dass die Anzeige jederzeit auf 0 gestellt werden kann.
Variante 1:
Bei dieser einfachen Variante kommt ein 16×2 I2C display zum Einsatz.
Experiment zum Newtonmeter
Mit dem Newtonmeter lässt sich zum Beispiel die Abstandsabhängigkeit der Kraft zwischen zwei Magneten bestimmen. Dazu postiert man einen der beiden Magnete auf dem Biegebalken und diesem nähert man sich nun mit dem zweiten Magneten. Die Kraft F in Abhängigkeit von der Entfernung r wird dann notiert.
Wie findet man nun aber das Abstandsgesetz für die Magnetkraft? Bei punktförmigen elektrischen Ladungen gilt ja das bekannte Coulombgesetz mit seiner 1/r²-Abhängigkeit:
Kennen wir die Wertepaare F(r), so können wir daraus die Potenz der Abstandsabhängigkeit ermitteln. Dazu trägt man den ln(F) gegen den ln(r) auf. Es müsste sich eine fallende Gerade ergeben. Deren negative Steigung –n entspricht dann der Potenz 1/r^n der Kraft zwischen den beiden Magneten.
Im konkreten Fall beträgt die Steigung der Geraden –3.78. Daher gilt folgende Abstandsabhängigkeit der Magnetkraft:
Die Kraft nimmt also deutlich stärker als beim Coulombgesetz ab, wie man anhand des folgenden Graphen F(r) auch sehen kann:
Wie kann man sich diese ≈ 1/r ^ 4–Abhängigkeit erklären? Nun, Magnete liegen ja immer als Dipole vor, es kommt also immer ein Nordpol in Kombination mit einem Südpol vor. Nehmen wir an, wir hätten zwei Magnete/Dipole im Abstand x zueinander und die beiden jeweils gekoppelten Magnetpole besitzen den Abstand d:
Nehmen wir weiters an, die Kraft zwischen den einzelnen Polen nehme mit 1/x² ab, also gleich wie das Coulombgesetz. Dann lautet die Formel für die Kraft F(x):
F(x) = 1/(x – d)² – 2/x² + 1/(x + d)².
Nun ein wenig Mathematik:
Für große Abstände x im Verhältnis zu d nimmt also die Kraft mit der vierten Potenz von x ab. Diesen Umstand kann man auch in Excel herleiten, indem wir für obige Kraftformel F(x) den ln(F) gegen den ln(x) auftragen. Die Steigung der so erhaltenen Geraden entspricht dann wieder der Potenz, konkret also –4:
Demnach liege ich mit „meiner“ experimentellen Potenz von n = –3.78 gar nicht so verkehrt, Heureka 😉
Arduino-Code:
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 |
// ================================================================================================== // Sensor HX711 zur Auwertung eines Waagesensors mit einer Auflösung von zumindest 0,01 g // ================================================================================================== #include <LiquidCrystal_I2C.h> #include <Wire.h> LiquidCrystal_I2C lcd(0x27,16,2); // set the LCD address to 0x27 for a 16 chars and 2 line display // Anschlüsse: // GND - GND // VCC - 5V // SDA - ANALOG Pin 4 // SCL - ANALOG pin 5 #include <HX711.h> HX711 scale(A1, A0); long Rohwert; // vom HX711 gelieferter Rohwert long offset = -115920; // offset float Scale = 6647.4; // scale float Masse; // berechnete Masse in g const int buttonPin = 2; // the number of the pushbutton pin int buttonState = 0; // variable for reading the pushbutton status // ===================== // ======= SETUP ======= // ===================== void setup() { Serial.begin(115200); scale.set_gain(128); // A-Kanal: gain = 64 oder 128; B-Kanal: fix 32 pinMode(buttonPin, INPUT); lcd.init(); // initialize the lcd // Print a message to the LCD. lcd.backlight(); lcd.setCursor(4,0); lcd.print("mg-Waage"); delay(3000); lcd.setCursor(4,0); lcd.print(" "); lcd.setCursor(0,0); lcd.print("m = "); } // ============================ // ======= HAUPTSCHLEIFE ====== // ============================ void loop() { buttonState = digitalRead(buttonPin); // button for new tara (offset) if (buttonState == HIGH) { offset = scale.read_average(20); // Einlesen von 20 Rohwerten zur Ermittlung des tara (offset) } Rohwert = scale.read_average(20); // Einlesen von 20 Rohwerten Masse = (Rohwert - offset) / Scale; // Berechnung der Masse in g /* Serial.print("Wert = "); Serial.println(Rohwert); */ Serial.print("Masse = "); Serial.println(Masse,2); lcd.setCursor(4,0); lcd.print(" "); lcd.setCursor(4,0); lcd.print(Masse,2); lcd.print(" g"); delay(50); } |
Variante 2:
Bei dieser Variante kommt anstelle des 16×2 displays ein 320×480 pixel display zum Einsatz. Damit lässt sich der Kraftverlauf F(t) graphisch darstellen. Erfasst werden bei meiner Variante rund 30 Sekunden. Der Messwert kann wieder jederzeit mittels Taster auf 0 gesetzt werden. Mit einem weiteren Taster startet man die Aufzeichnung. Im Moment ist ein 1kg-Biegebalken in Verwendung. Damit lassen sich Kräfte zwischen 0-10 N erfassen. Bei Verwendung eines anderen Biegebalkens können natürlich auch andere Bereiche erfasst werden.
Mit diesem Newtonmeter kann man einige physikalische Experimente durchführen. Zum Beispiel die Bestimmung der Oberflächenspannung mittels Ringmethode oder die Bestimmung der Haft- und Gleitreibung. Hier erkennt man schön, dass etwa die Haftreibung größer als die Gleitreibung ist und beide von der Masse/Normalkraft des zu ziehenden Objekts abhängen.
Hier die Bestimmung der Oberflächenspannung von Wasser mit der Ringmethode. Es ist darauf zu achten, den doppelten Kreisumfang in die Formel für die Oberflächenspannung σ einzusetzen, da die Oberflächenhaut sowohl auf der Ringinnenseite, als auch auf der Ringaußenseite angreift!
Ein Vergleich mit Tabellenwerten zeigt die recht gute Übereinstimmung zwischen Theorie und Experiment:
Quelle: https://de.wikipedia.org/wiki/Oberflächenspannung
Arduino-Code:
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 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 |
#include <UTFT.h> #include <Wire.h> #include <HX711.h> HX711 scale(A1, A0); long Rohwert; // vom HX711 gelieferter Rohwert long offset = 6475; // offset float Scale = 90413; // scale: m = 17.4 g = 0.170694 N increase the value from 6475 to 21908 (difference = 15433). Therefore 1 N would increase the value by 90413 = scale float Kraft_alt, Kraft_neu; // berechnete Kraft in N float Kraft_max; // maximale Kraft // Declare which fonts we will be using extern uint8_t SmallFont[]; UTFT myGLCD(ILI9486,38,39,40,41); int i, j; // Zählvariable const int offsetPin = 2; // the number of the pushbutton pin int offsetState = 0; // variable for reading the pushbutton status const int startPin = 3; // the number of the switch pin int startState = 0; // variable for reading the switch status // ==================================== // === === // === SETUP === // === === // ==================================== void setup() { Serial.begin(9600); // while(!Serial); // time to get serial running pinMode(offsetPin, INPUT); pinMode(startPin, INPUT); // Setup the LCD myGLCD.InitLCD(); myGLCD.setFont(SmallFont); myGLCD.clrScr(); myGLCD.setColor(255, 255, 0); myGLCD.fillRect(0, 0, 479, 13); myGLCD.setColor(0, 0, 0); myGLCD.setBackColor(255, 255, 0); myGLCD.print("Newtonmeter - stoppi", CENTER, 1); scale.set_gain(128); // A-Kanal: gain = 64 oder 128; B-Kanal: fix 32 Kraft_alt = 0.0; } // ==================================== // === === // === HAUPTSCHLEIFE === // === === // ==================================== void loop() { while(startState = digitalRead(startPin)) // Warten bis start-Taster betätigt wurde { /* Rohwert = 0.0; for(j = 1; j <= 100; j++) { Rohwert = Rohwert + scale.read(); // Einlesen eines Rohwerts delay(10); } Rohwert = Rohwert / 100.0; Serial.println(Rohwert); delay(20); */ Rohwert = scale.read(); // Einlesen eines Rohwerts Kraft_neu = (Rohwert - offset) / Scale; // Berechnung der Kraft in Newton //Serial.println(Kraft_neu); myGLCD.setColor(0, 255, 0); myGLCD.setBackColor(0, 0, 0); myGLCD.print(" ", 70, 18); myGLCD.printNumF(Kraft_neu, 2, 70, 18); offsetState = digitalRead(offsetPin); // button for new tara (offset) if (offsetState == LOW) { offset = scale.read(); // Einlesen eines Rohwerts zur Ermittlung des offsets } delay(20); } Kraft_alt = 0.0; Kraft_max = 0.0; myGLCD.clrScr(); myGLCD.setColor(255, 255, 0); myGLCD.fillRect(0, 0, 479, 13); myGLCD.setColor(0, 0, 0); myGLCD.setBackColor(255, 255, 0); myGLCD.print("Newtonmeter - stoppi", CENTER, 1); // Zeichnen der beiden Achsen myGLCD.setColor(255, 255, 255); myGLCD.drawLine(50,290,430,290); myGLCD.drawLine(50,20,50,290); myGLCD.setColor(255, 255, 255); myGLCD.setBackColor(0, 0, 0); myGLCD.print("F[N]", 5, 18); myGLCD.setColor(255, 255, 255); myGLCD.setBackColor(0, 0, 0); myGLCD.print("t[sec]", 420, 300); myGLCD.setColor(255, 255, 255); myGLCD.drawLine(45,290,50,290); myGLCD.drawLine(45,240,50,240); myGLCD.drawLine(45,190,50,190); myGLCD.drawLine(45,140,50,140); myGLCD.drawLine(45,90,50,90); myGLCD.drawLine(45,40,50,40); myGLCD.drawLine(50,290,50,295); myGLCD.drawLine(100,290,100,295); myGLCD.drawLine(150,290,150,295); myGLCD.drawLine(200,290,200,295); myGLCD.drawLine(250,290,250,295); myGLCD.drawLine(300,290,300,295); myGLCD.drawLine(350,290,350,295); myGLCD.drawLine(400,290,400,295); myGLCD.setColor(255, 255, 255); myGLCD.setBackColor(0, 0, 0); myGLCD.printNumI(0,30,285); myGLCD.printNumI(2,30,235); myGLCD.printNumI(4,30,185); myGLCD.printNumI(6,30,135); myGLCD.printNumI(8,30,85); myGLCD.printNumI(10,25,35); myGLCD.printNumI(0,48,300); myGLCD.printNumI(4,98,300); myGLCD.printNumI(8,148,300); myGLCD.printNumI(12,194,300); myGLCD.printNumI(16,244,300); myGLCD.printNumI(20,294,300); myGLCD.printNumI(24,344,300); myGLCD.printNumI(28,394,300); // graues Gitternetz zeichnen myGLCD.setColor(120, 120, 120); myGLCD.drawLine(51,265,430,265); myGLCD.drawLine(51,240,430,240); myGLCD.drawLine(51,215,430,215); myGLCD.drawLine(51,190,430,190); myGLCD.drawLine(51,165,430,165); myGLCD.drawLine(51,140,430,140); myGLCD.drawLine(51,115,430,115); myGLCD.drawLine(51,90,430,90); myGLCD.drawLine(51,65,430,65); myGLCD.drawLine(51,40,430,40); myGLCD.drawLine(100,40,100,289); myGLCD.drawLine(150,40,150,289); myGLCD.drawLine(200,40,200,289); myGLCD.drawLine(250,40,250,289); myGLCD.drawLine(300,40,300,289); myGLCD.drawLine(350,40,350,289); myGLCD.drawLine(400,40,400,289); myGLCD.setColor(0, 255, 0); for(i = 1; i <= 380; i++) { offsetState = digitalRead(offsetPin); // button for new tara (offset) if (offsetState == LOW) { offset = scale.read(); // Einlesen eines Rohwerts zur Ermittlung des offsets Kraft_max = 0.0; } //Rohwert = scale.read_average(2); // Einlesen der gemittelten Rohwerten Rohwert = scale.read(); // Einlesen eines Rohwerts Kraft_neu = (Rohwert - offset) / Scale; // Berechnung der Kraft in Newton if(Kraft_neu > Kraft_max) { Kraft_max = Kraft_neu; myGLCD.setColor(255, 0, 0); myGLCD.print(" ", 180, 18); myGLCD.printNumF(Kraft_max, 3, 180, 18); myGLCD.setColor(0, 255, 0); } //Serial.println(Rohwert); myGLCD.print(" ", 70, 18); myGLCD.printNumF(Kraft_neu, 2, 70, 18); if(Kraft_neu >= 0 && Kraft_neu <= 10) { //myGLCD.drawPixel(50 + i, 290 - Kraft * 25); myGLCD.drawLine(50 + (i - 1),290 - Kraft_alt * 25,50 + i,290 - Kraft_neu * 25); Kraft_alt = Kraft_neu; } } } |
Variante 3:
Für die Erfassung des Schubs eines Raketentreibsatzes musste ich die Variante 2 ein wenig anpassen. Beim HX711-Modul habe ich nun den 80Hz Modus aktiviert und dafür die Zeitachse am Display auf ca. 0 – 8 Sekunden abgeändert.
Die Treibsätze gibt es bei der Firma Klima in Deutschland, wobei ich mich für den Typ C2-P entschieden habe. Dieser hat eine Schubkraft von ca. 2 N und eine Schubdauer von rund 5 Sekunden.
Der Treibsatz wird über Metallwinkel an die Wägezelle gekoppelt:
Da mir der Versuch in der Wohnung doch zu „brand“gefährlich erschien, ging ich zur Durchführung in den Hof. Beim ersten Durchgang kippte mir die Halterung aufgrund des doch recht beachtlichen Schubs um. Beim zweiten Versuch klappte dann alles wie gewünscht…
Hier der erhaltene Kraftverlauf, welcher sich sehr gut mit den Angaben des Herstellers deckt, Heureka 😉 Die anfängliche Spitzenkraft erreichte rund 6 N, danach betrug die Kraft für ca. 5 Sekunden 2 N…
Das Youtube-Video:
Der leicht abgeänderte Arduino-Code:
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 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 |
#include <UTFT.h> #include <Wire.h> #include <HX711.h> HX711 scale(A1, A0); long Rohwert; // vom HX711 gelieferter Rohwert long offset = 6475; // offset float Scale = 198388; // scale: m = 17.4 g = 0.170694 N increase the value from 6475 to 21908 (difference = 15433). Therefore 1 N would increase the value by 90413 = scale float Kraft_alt, Kraft_neu; // berechnete Kraft in N float Kraft_max; // maximale Kraft // Declare which fonts we will be using extern uint8_t SmallFont[]; UTFT myGLCD(ILI9486,38,39,40,41); int i, j; // Zählvariable const int offsetPin = 2; // the number of the pushbutton pin int offsetState = 0; // variable for reading the pushbutton status const int startPin = 3; // the number of the switch pin int startState = 0; // variable for reading the switch status // ==================================== // === === // === SETUP === // === === // ==================================== void setup() { Serial.begin(9600); // while(!Serial); // time to get serial running pinMode(offsetPin, INPUT); pinMode(startPin, INPUT); // Setup the LCD myGLCD.InitLCD(); myGLCD.setFont(SmallFont); myGLCD.clrScr(); myGLCD.setColor(255, 255, 0); myGLCD.fillRect(0, 0, 479, 13); myGLCD.setColor(0, 0, 0); myGLCD.setBackColor(255, 255, 0); myGLCD.print("Raketenschub - stoppi", CENTER, 1); scale.set_gain(128); // A-Kanal: gain = 64 oder 128; B-Kanal: fix 32 Kraft_alt = 0.0; } // ==================================== // === === // === HAUPTSCHLEIFE === // === === // ==================================== void loop() { Kraft_neu = 0.0; while(Kraft_neu < 0.2) // Warten bis die Kraft >= 0.2 N beträgt { /* Rohwert = 0.0; for(j = 1; j <= 100; j++) { Rohwert = Rohwert + scale.read(); // Einlesen eines Rohwerts delay(10); } Rohwert = Rohwert / 100.0; Serial.println(Rohwert); delay(20); */ Rohwert = scale.read(); // Einlesen eines Rohwerts Kraft_neu = (Rohwert - offset) / Scale; // Berechnung der Kraft in Newton //Serial.println(Kraft_neu); myGLCD.setColor(0, 255, 0); myGLCD.setBackColor(0, 0, 0); myGLCD.print(" ", 70, 18); myGLCD.printNumF(Kraft_neu, 2, 70, 18); offsetState = digitalRead(offsetPin); // button for new tara (offset) if (offsetState == LOW) { offset = scale.read(); // Einlesen eines Rohwerts zur Ermittlung des offsets } delay(20); } Kraft_alt = 0.0; Kraft_max = 0.0; myGLCD.clrScr(); myGLCD.setColor(255, 255, 0); myGLCD.fillRect(0, 0, 479, 13); myGLCD.setColor(0, 0, 0); myGLCD.setBackColor(255, 255, 0); myGLCD.print("Raketenschub - stoppi", CENTER, 1); // Zeichnen der beiden Achsen myGLCD.setColor(255, 255, 255); myGLCD.drawLine(50,290,430,290); myGLCD.drawLine(50,20,50,290); myGLCD.setColor(255, 255, 255); myGLCD.setBackColor(0, 0, 0); myGLCD.print("F[N]", 5, 18); myGLCD.setColor(255, 255, 255); myGLCD.setBackColor(0, 0, 0); myGLCD.print("t[sec]", 420, 300); myGLCD.setColor(255, 255, 255); myGLCD.drawLine(45,290,50,290); myGLCD.drawLine(45,240,50,240); myGLCD.drawLine(45,190,50,190); myGLCD.drawLine(45,140,50,140); myGLCD.drawLine(45,90,50,90); myGLCD.drawLine(45,40,50,40); myGLCD.drawLine(50,290,50,295); myGLCD.drawLine(95,290,95,295); myGLCD.drawLine(140,290,140,295); myGLCD.drawLine(185,290,185,295); myGLCD.drawLine(230,290,230,295); myGLCD.drawLine(275,290,275,295); myGLCD.drawLine(320,290,320,295); myGLCD.drawLine(365,290,365,295); myGLCD.drawLine(410,290,410,295); myGLCD.setColor(255, 255, 255); myGLCD.setBackColor(0, 0, 0); myGLCD.printNumI(0,30,285); myGLCD.printNumI(2,30,235); myGLCD.printNumI(4,30,185); myGLCD.printNumI(6,30,135); myGLCD.printNumI(8,30,85); myGLCD.printNumI(10,25,35); myGLCD.printNumI(0,48,300); myGLCD.printNumI(1,93,300); myGLCD.printNumI(2,138,300); myGLCD.printNumI(3,183,300); myGLCD.printNumI(4,228,300); myGLCD.printNumI(5,273,300); myGLCD.printNumI(6,318,300); myGLCD.printNumI(7,363,300); myGLCD.printNumI(8,408,300); // graues Gitternetz zeichnen myGLCD.setColor(120, 120, 120); myGLCD.drawLine(51,265,430,265); myGLCD.drawLine(51,240,430,240); myGLCD.drawLine(51,215,430,215); myGLCD.drawLine(51,190,430,190); myGLCD.drawLine(51,165,430,165); myGLCD.drawLine(51,140,430,140); myGLCD.drawLine(51,115,430,115); myGLCD.drawLine(51,90,430,90); myGLCD.drawLine(51,65,430,65); myGLCD.drawLine(51,40,430,40); myGLCD.drawLine(95,40,95,289); myGLCD.drawLine(140,40,140,289); myGLCD.drawLine(185,40,185,289); myGLCD.drawLine(230,40,230,289); myGLCD.drawLine(275,40,275,289); myGLCD.drawLine(320,40,320,289); myGLCD.drawLine(365,40,365,289); myGLCD.drawLine(410,40,410,289); myGLCD.setColor(0, 255, 0); for(i = 1; i <= 380; i++) { offsetState = digitalRead(offsetPin); // button for new tara (offset) if (offsetState == LOW) { offset = scale.read(); // Einlesen eines Rohwerts zur Ermittlung des offsets Kraft_max = 0.0; } //Rohwert = scale.read_average(2); // Einlesen der gemittelten Rohwerten Rohwert = scale.read(); // Einlesen eines Rohwerts Kraft_neu = (Rohwert - offset) / Scale; // Berechnung der Kraft in Newton if(Kraft_neu > Kraft_max) { Kraft_max = Kraft_neu; myGLCD.setColor(255, 0, 0); myGLCD.print(" ", 180, 18); myGLCD.printNumF(Kraft_max, 3, 180, 18); myGLCD.setColor(0, 255, 0); } //Serial.println(Rohwert); myGLCD.print(" ", 70, 18); myGLCD.printNumF(Kraft_neu, 2, 70, 18); if(Kraft_neu >= 0 && Kraft_neu <= 10) { //myGLCD.drawPixel(50 + i, 290 - Kraft * 25); myGLCD.drawLine(50 + (i - 1),290 - Kraft_alt * 25,50 + i,290 - Kraft_neu * 25); Kraft_alt = Kraft_neu; } } while(startState = digitalRead(startPin)) // Warten bis start-Taster betätigt wurde { } } |