8x8-LED-Matrix mit Beschleunigungssensor / Kompass und 5 Funktionen

Die 8x8-LED-Matrix aus den letzten Projekten hat Zuwachs bekommen: ein LSM303C-Modul, das ich auf meiner Raspberry-Seite ja schon genauer unter die Lupe genommen hatte..

Der LSM303C hat einen Beschleunigungssensor (auf neudeutsch Accelerometer), einen Kompass (Magnetometer) und ein Thermometer integriert und wird über den I2C-Bus angesteuert.

Der I2C-Bus auf dem Arduino

Den I2C Bus kennen wir ja schon Raspberry. Auf dem Arduino ist hierfür die Wire.h-Library zuständig, die wir am Programmanfang mit #include <Wire.h> inkludieren.

Wire kommuniziert über die Ports A4 für SDA (serial data) A5 für SCL (serial clock) mit dem I2C-Bus. Diese Anschlüsse sind hart kodiert und nicht so einfach änderbar. Warum man hier zwei der knappen Analog-Ports (6 an der Zahl) und nicht zwei der Digital-Ports, von den es immerhin 14 gibt, benutzt, habe ich noch nicht verstanden. Aber es ist nun einmal so.

Wire kennt nur ein paar wichtige Befehle, die man beherrschen muss. Am besten ist das in einem kleinen Beispiel erklärt: void i2cWrite(byte deviceAddr, byte channelAddr, byte value) { Wire.beginTransmission(deviceAddr); Wire.write(channelAddr); Wire.write(value); Wire.endTransmission(); } Diese Funktion schickt ein einzelnes Byte value an das Gerät mit der Adresse deviceAddr, damit dieses es dort am Speicherplatz channelAddr ablegt. Dazu wird eine Verbindung mit beginTransmission aufgemacht, das Speicheradressebyte vorausgeschickt (write(channelAddr);), worauf auch schon der zu speichernde Wert folgt (write(value);). Damnit ist alles erledigt und die Verbinund kann wieder mit endTransmission(); beendet werden.

uint8_t i2cReadByte(int deviceAddr, int channelAddr) { Wire.beginTransmission(deviceAddr); // sagen, ab welcher Channel-Adresse wir lesen möchten Wire.write(channelAddr); Wire.endTransmission(); Wire.requestFrom(deviceAddr,1); // x Bytes auf diesem Channel anfordern while (Wire.available()==0) { // auf Ankunft warten delayMicroseconds(100); } uint8_t by = Wire.read(); // Ein Byte empfangen return by; } Werte können mit Wire nur einzeln Byte für Byte gelesen werten. Das erledigt der Befehl read();. Zuvor muss man dem Gerät allerdings sagen, ob wo (write(channelAddr);) man wieviele (requestFrom(deviceAddr,1);) Bytes haben will. Danach wartet man darauf, dass die Bytes eintrudeln (While-Schleife) und kann sie dann lesen.

Das ist auch schon der ganze Zauber. Diese beiden Funktionen kann man noch weiter kapseln, z. B., um ganze Wörter oder Blöcke zu schreiben.

Die erweiterte Platine


Auf der Prototype-Shield-Platine geht es schon recht eng zu, aber oben links habe ich doch noch ein Plätzchen gefunden, um einen 5-poligen Sockel einzulöten, auf dem die 5 interessanten Pins des Moduls Platz finden:




Den auf dem Shield bereits befindlichen Taster habe ich an D12 gehängt. Ein Taster reicht aus, wenn man auswertet, ob er lang oder kurz gedrückt wird. Außerdem gibt es noch den sowieso schon vorhandenen Reset-Taster, mit dem man neu starten kann. Damit gelangt man also wieder zurück ins Hauptmenü.

Das Programm geht nach dem Reset in den Schlafmodus. Den musste ich einbauen, weil es immer wieder Probleme mit dem Upload von Sketches auf den Arduino gab und ich meine, dass könnte gut daran liegen, dass die Schaltung beim Scroller so viel Strom verbraucht. Darum macht der Uno im Schlafmodus nichts weiter als auf einen Tastendruck zu warten und legt dann erst los. Nach einem Reset ist der Stromverbrauch also unten und bisher hatte ich seitdem auch keine Upload-Probleme mehr.

Nach dem ersten Tastendruck läuft der Scroller durch, der die Funktionen anzeigt: 1. Nerd-Uhr, 2. Tilt-Demo, 3. Kompass, 4. Gaußmeter, 5. Thermometer.

Nach weiterem Tastendruck blinken die ersten 5 LEDs und man muss eine von ihnen für die Funktionsnr. auswählen. Kurz drücken -> nächste Option. Lang drücken -> Option wählen.

Die Nerd-Uhr

Die dürfte bereits aus dem Projekt 8x8-LED-Matrix als Geek Uhr verwenden (Zeit über Serial) bekannt sein. Statt über den Serial Port und die USB-Verbindung kann ich nun die Stunden und Minuten über die Taste einstellen. Der Rest bleibt gleich.

Tilt-Demo

Hier wird der Beschleunigungssensor als Neigungssensor benutzt. Denn es wirkt ja immer eine Beschleunigung mit 1 g zum Erdmittelpunkt hin. Und je, wie man den Sensor dreht, verteilt sich diese normalerweise auf der Z-Achse liegende Beschleunigung auf die anderen Achsen. So kann man den Kippwinkel feststellen, der dann durch die Lage eines Punkt auf dem 8x8-Display angezeigt wird. Es sieht dann so aus, als wäre da eine Kugel auf der Tischplatte, die je nachdem wie man die Tischplatte kippt, an den Rand rollt.

Zu Anfang wir ein großes + angezeigt, dass einem mitteilen soll: Gerät still und horizontal halten. Denn in den ersten 2 Sekunden erfolgt die Kablibrierung. Der nachfolgende Anzeige ist dann relativ zum Ruhezustand.

Kompass

Hier wird das Magnetometer gebraucht. Er macht sich zunutze, dass der Wert auf der X und Y-Achse jeweils am höchsten ist, wenn die Achsen mit den Erdmagnetfeldlinien ausgerichtet sind.

Zu Anfang wird ein + angezeigt, um das sich ein Punkt dreht. Das soll heißen: Horizontal halten und einmal im Kreis drehen. Dabei erfolgt die Kalibrierung durch Messwerterfassung. Danach kann berechnet werden, wie sehr man am gemessenen Maximalwert (also Norden) ist und so die Himmelsrichtung ausrichten. Ein heller Punkt zeigt danach Norden an. Zudem wird die Himmelsrichtung angezeigt und zwar in Richtung der Y-Achse (sieht Sensor-Aufdruck bzw. der Taster zeigt zum Nutzer).

Gaußmeter / Feldstärkemesser

Dieser nutzt das Magnetometer auf einfache Weise und zeigt einfach die Summe der gemessenen Feldstärken von allen drei Achsen an. Je höher der Wert, je mehr LEDs leuchten. Damit kann man elektromagnetische Strahlung messen oder erkennen, ob magnetische oder magnetisierbare Dinge in der Nähe sind.

Thermometer

Das Thermometer im LSM303C ist zum einen nicht sonderlich genau (nur 1/4 Grad) und springt zudem sehr, falls nicht genügend Strom zur Verfügung steht. Mit Batterie funktioniert es nur unzuverlässig. Erst beim Anschluss eines Netzteiles wird es genau. die Anzeige erfolgt wieder mit erleuchteten Punkte. Je Punkt ein Grad Celsius.

Das Ergebnis



Im Video führe ich die Funktionen kurz vor. Leider ist nicht immer gut zu erkennen, was auf der Matrix dargestellt wird, besonders beim Scroller ist das Mitlesen etwas schwierig. Da hilft es etwas, das Videofenster kleiner zu machen.

Der Source-Code

//////////////////////////////////////////////////////// // (C) 2018 by Oliver Kuhlemann // // Bei Verwendung freue ich mich über Namensnennung, // // Quellenangabe und Verlinkung // // Quelle: http://cool-web.de/arduino/ // //////////////////////////////////////////////////////// #include <stdio.h> #include <stdint.h> #include <unistd.h> #include <time.h> #include <string.h> #include <Wire.h> // I2C hängt am Uno immer an A4 (SDA) und A5 (SCL), max read/write: 32 byte #define PinData 2 // HC595 für 8x8-LED-Matrix #define PinClockStore 3 // HC595 für 8x8-LED-Matrix #define PinClockShift 4 // HC595 für 8x8-LED-Matrix #define ClockPulseLen 1 // HC595 in Microsekunden #define PinTaster 12 uint8_t good[8] = { 0b00000000, // Checkmark = okay 0b00000011, 0b00000110, 0b00001100, 0b11011000, 0b11110000, 0b01100000, 0b00000000 }; uint8_t bad[8] = { 0b00000000, // X = Fehler 0b11000011, 0b01100110, 0b00111100, 0b00011000, 0b00111100, 0b01100110, 0b11000011 }; void setup() { // put your setup code here, to run once: pinMode(PinData, OUTPUT); pinMode(PinClockStore, OUTPUT); pinMode(PinClockShift, OUTPUT); pinMode(PinTaster, INPUT_PULLUP); Wire.begin(); // join i2c bus (address optional for master) } void i2cWrite(byte deviceAddr, byte channelAddr, byte value) { Wire.beginTransmission(deviceAddr); Wire.write(channelAddr); Wire.write(value); Wire.endTransmission(); } uint8_t i2cReadByte(int deviceAddr, int channelAddr) { Wire.beginTransmission(deviceAddr); // sagen, ab welcher Channel-Adresse wir lesen möchten Wire.write(channelAddr); Wire.endTransmission(); Wire.requestFrom(deviceAddr,1); // x Bytes auf diesem Channel anfordern while (Wire.available()==0) { // auf Ankunft warten delayMicroseconds(100); } uint8_t by = Wire.read(); // Ein Byte empfangen return by; } int16_t i2cReadSWordLoHi(int deviceAddr, int channelAddr) { // Unsigned Word Wire.beginTransmission(deviceAddr); // sagen, ab welcher Channel-Adresse wir lesen möchten Wire.write(channelAddr); Wire.endTransmission(); Wire.requestFrom(deviceAddr,2); // x Bytes auf diesem Channel anfordern uint8_t by1 =0x80; uint8_t by2 =0x80; int16_t wo = 0; while (Wire.available() <2) { // auf ankunft warten delayMicroseconds(100); } by1 = Wire.read(); // Ein Byte empfangen by2 = Wire.read(); // Ein Byte empfangen wo = by2*256 + by1; return wo; } void storeTick() { // einen Puls auf die ClockStore-Leitung schicken digitalWrite(PinClockStore, HIGH); delayMicroseconds (ClockPulseLen); digitalWrite(PinClockStore, LOW); delayMicroseconds (ClockPulseLen); } void shiftTick() { // einen Puls auf die ClockShift-Leitung schicken digitalWrite(PinClockShift, HIGH); delayMicroseconds (ClockPulseLen); digitalWrite(PinClockShift, LOW); delayMicroseconds (ClockPulseLen); } void clearDots() { // alle Punkte löschen for (int i = 0; i < 16; i++) { digitalWrite(PinData, LOW); shiftTick(); // nächstes Bit } storeTick(); // alle Bits fertig. Speichern } void lightCol(uint8_t col, uint8_t colByte) { // Vorwiderstände hängen an den Row-Ausgängen, sonst keine gleichmäßige Helligkeit // zuerst das colByte invertiert for (uint8_t b = 0; b < 8; b++) { uint8_t w = (b == (7 - col)); w ^= 1; // invertieren, weil wird müssen auf Low und damit zu GND durchschalten digitalWrite(PinData, w); // w = 0 | 1 shiftTick(); // nächstes Bit } // dann das rowByte einschieben for (uint8_t b = 0; b < 8; b++) { uint8_t w = (colByte & 1); digitalWrite(PinData, w); // w = 0 | 1 shiftTick(); // nächstes Bit colByte = colByte >> 1; } storeTick(); // alle Bits fertig. Speichern } void lightDot(uint8_t x, uint8_t y) { lightCol(x, 1 << (7 - y)); } void lightMatrix(uint8_t *rowArray, unsigned long msecs) { uint8_t bit=0; uint8_t wert; uint8_t colArray[8] = {0, 0, 0, 0, 0, 0, 0, 0}; for (uint8_t col = 0; col < 8; col++) { bit=1<<(7-col); for (uint8_t row = 0; row < 8; row++) { wert = (rowArray[row] & bit); if (wert > 0) colArray[col] += 1<<(7-row); } } for (unsigned long mStop = millis() + msecs; millis() < mStop; ) { for (uint8_t col = 0; col < 8; col++) { lightCol(col, colArray[col]); delayMicroseconds(500); } } clearDots(); } void scrollShow (uint8_t colArray[8], uint16_t verzoegerung) { for (uint16_t i=0; i < verzoegerung/6.6; i++) { // und anzeigen for (uint8_t col=0; col<8; col++) { lightCol(col, colArray[col]); delayMicroseconds(500); if (digitalRead(PinTaster) == LOW) return; } clearDots(); } } void scroll (char *text[8], uint16_t cols, uint16_t verzoegerung) { // cols=Anzahl von Spalten gesamt uint8_t colArray[8] = {0, 0, 0, 0, 0, 0, 0, 0}; //ersten Screen reinschieben for (uint8_t c=0; c<7; c++) { for (uint8_t i=0; i<7;i++) { colArray[i]=colArray[i+1]; } uint8_t colByte=0; for (uint8_t row=0; row<8; row++) { if (text[row][c] == '#') colByte += 1<<(7-row); } colArray[7]=colByte; scrollShow(colArray,verzoegerung); if (digitalRead(PinTaster) == LOW) return; } // gesamten Text durchscrollen for (uint16_t col=0; col < cols-8; col++) { for (uint16_t c=col; c<col+8; c++) { // 8 ZeilenBytes holen uint8_t colByte=0; for (uint8_t row=0; row<8; row++) { if (text[row][c] == '#') colByte += 1<<(7-row); } colArray[c-col] = colByte; } scrollShow(colArray,verzoegerung); } // letzten Screen auch noch rausschieben for (uint8_t c=0; c<8; c++) { for (uint8_t i=0; i<7;i++) { colArray[i]=colArray[i+1]; } colArray[7]=0; scrollShow(colArray,verzoegerung); } } long waitTaster(long maxWait) { // gibt zurück, wieviele msecs der Taster gedrückt gehalten war long msecs=0; long start=millis(); while (digitalRead(PinTaster) == HIGH) { delay (1); msecs = millis()-start; if (maxWait != 0 && maxWait > msecs) return 0; } // wielange gehalten ? start=millis(); while (digitalRead(PinTaster) == LOW) { delay (1); } msecs = millis()-start; return msecs; } // ----------------------------------------- void nerdUhr() { uint8_t zeit[8]={ 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000 }; clearDots(); lightMatrix (good, 1000); // kleine Warte-Animation /* 0 1 2 3 4 5 6 7 . . # # # # . . 0 . # . . . . # . 8 # . . . . . . # 16 # . . . . . . # 24 # . . . . . . # 32 # . . . . . . # 40 . # . . . . # . 48 . . # # # # . . 56 */ uint8_t dots[20] = {2, 3, 4, 5, 14, 23, 31, 39, 47, 54, 61, 60, 59, 58, 49, 40, 32, 24, 16, 9}; boolean okay = false; long h=-1; long m=-1; long s=-1; uint32_t secsOnSet=-1; // warten auf Taster für Stunden while (digitalRead(PinTaster) == HIGH) { for (uint8_t i=0;i<24;i++) { lightDot(i%8,i/8); delay(25); if (digitalRead(PinTaster) == LOW) break; } } h=0; while (1) { long ms=waitTaster(60000); if (ms > 500) { break; } else { h++; if (h>23) h=0; while (digitalRead(PinTaster) == HIGH) { for (uint8_t i=0;i<h;i++) { lightDot(i%8,i/8); } } } } // warten auf Taster für Minuten while (digitalRead(PinTaster) == HIGH) { for (uint8_t i=24;i<24+32;i++) { lightDot(i%8,i/8); delay(25); if (digitalRead(PinTaster) == LOW) break; } } m=0; while (1) { long ms=waitTaster(60000); if (ms > 500) { break; } else { m+=1; if (m>32) m=0; while (digitalRead(PinTaster) == HIGH) { for (uint8_t i=24;i<24+m;i++) { lightDot(i%8,i/8); } } } } m=m*60/32; if (m >= 60) m=59; s=0; // checkmark zur Bestätigung clearDots(); lightMatrix (good, 2000); clearDots(); long hours=h; long minutes=m; long seconds=s; secsOnSet = millis()/1000.; while (1) { // Zeit anzeigen long secsSince0 = (hours*3600. + minutes*60. + seconds + 1) + millis()/1000. - secsOnSet; h = secsSince0 / 3600; secsSince0 -= h*3600; h = h % 24; // 24 Uhr ist wieder 0 Uhr m = secsSince0 / 60; secsSince0 -= m*60; s = secsSince0; // oooooooo 1-8 h // oooooooo 9-16 h // ooooooo. 17-23h // oooooooo 1-15 m // oooooooo 16-30 m // oooooooo 31-45 m // oooooooo 46-59 m // ..oooooo 60 Sek binär for (int i=0; i<8; i++) { zeit[i]=0; } //stunden for (int i=0; i<8; i++) { if (h > i) zeit[0] |= (1<<(7-i)); } for (int i=0; i<8; i++) { if (h > i+8) zeit[1] |= (1<<(7-i)); } for (int i=0; i<8; i++) { if (h > i+16) zeit[2] |= (1<<(7-i)); } //minuten for (int i=0; i<8; i++) { if (m*8/15. > i) zeit[3] |= (1<<(7-i)); } for (int i=0; i<8; i++) { if (m*8/15. > i+8) zeit[4] |= (1<<(7-i)); } for (int i=0; i<8; i++) { if (m*8/15. > i+16) zeit[5] |= (1<<(7-i)); } for (int i=0; i<8; i++) { if (m*8/15. > i+24) zeit[6] |= (1<<(7-i)); } //sekunden zeit[7]=s; clearDots(); lightMatrix (zeit, 1000); } } // ---------------------------------------------------------------------------------------------- void initBeschl(){ i2cWrite(0x1d, 0x20, 0x27); } int16_t getBeschlX() { return i2cReadSWordLoHi(0x1d,0x28); } int16_t getBeschlY() { return i2cReadSWordLoHi(0x1d,0x2a); } int16_t getBeschlZ() { return i2cReadSWordLoHi(0x1d,0x2c); } void tilt() { lightMatrix (good, 1000); clearDots(); initBeschl(); // check sensor uint8_t ret=i2cReadByte(0x1d, 0x0f); if (ret != 0x41) { return -1; } int16_t bXMin=32767; int16_t bXMax=-32768; int16_t bYMin=32767; int16_t bYMax=-32768; int16_t bZMin=32767; int16_t bZMax=-32768; int16_t bXDelta=0; int16_t bYDelta=0; int16_t bZDelta=0; uint16_t za = 0; while (1) { if (za <30) za++; int16_t beschlX = getBeschlX(); if (beschlX < bXMin) bXMin=beschlX; if (beschlX > bXMax) bXMax=beschlX; float beschlXG = ((beschlX-bXDelta)*0.061); float beschlXMS2 = (beschlXG/1000.*9.81); int16_t beschlY = getBeschlY(); if (beschlY < bYMin) bYMin=beschlY; if (beschlY > bYMax) bYMax=beschlY; float beschlYG = ((beschlY-bYDelta)*0.061); float beschlYMS2 = (beschlYG/1000.*9.81); int16_t beschlZ = getBeschlZ(); if (beschlZ < bZMin) bZMin=beschlZ; if (beschlZ > bZMax) bZMax=beschlZ; float beschlZG = ((beschlZ-bZDelta)*0.061); float beschlZMS2 = (beschlZG/1000.*9.81); float kippX = (beschlX-bXDelta) / (16384.-bXDelta); if (kippX < -1) kippX= -1; if (kippX > 1) kippX= 1; float kippY = (beschlY-bYDelta) / (16384.-bYDelta); if (kippY < -1) kippY= -1; if (kippY > 1) kippY= 1; if (za == 20) { // Mittelwert als Delta bXDelta=(bXMin+bXMax)/2; bYDelta=(bYMin+bYMax)/2; bZDelta=(bZMin+bZMax)/2; } if (za < 20) { // Stillhalten anzeigen uint8_t pic[8] = {0x18,0x18,0x18,0xff,0xff,0x18,0x18,0x18}; clearDots(); lightMatrix(pic,100); clearDots(); } else { // Neigung anzeigen - Mitte ist beo Col 3, Row 3. Von da aus ein Viererpunkt zeichnen. Anzeige von -3 bis +3 int8_t x =round(kippX*-5); int8_t y =round(kippY*-5); if (x <-3) x=-3; if (x > 3) x= 3; if (y <-3) y=-3; if (y > 3) y= 3; for (int i=0; i <50; i++) { lightDot(3-x, 3-y); lightDot(4-x, 3-y); lightDot(3-x, 4-y); lightDot(4-x, 4-y); delayMicroseconds(5); clearDots(); } } } //endwhile (1) } // ---------------------------------------------------------------------------------------------- void initKompass(){ i2cWrite (0x1e, 0x20, 0x90); i2cWrite (0x1e, 0x22, 0x00); } float getTemp() { int16_t wort=i2cReadSWordLoHi(0x1e,0x2e); float temp = 25. + ((float) wort / 4.); // nur durch rumprobieren geschätzt, dürfte aber stimmen return temp; } int16_t getKompassX() { return i2cReadSWordLoHi(0x1e,0x28); } int16_t getKompassY() { return i2cReadSWordLoHi(0x1e,0x2a); } int16_t getKompassZ() { return i2cReadSWordLoHi(0x1e,0x2c); } void kompass() { initKompass(); lightMatrix (good, 1000); int16_t kXMin=32767; int16_t kXMax=-32768; int16_t kYMin=32767; int16_t kYMax=-32768; int16_t kZMin=32767; int16_t kZMax=-32768; while (1) { boolean kalibriert = false; int16_t kompassX = getKompassX(); if (!kalibriert) { if (kompassX < kXMin) kXMin=kompassX; if (kompassX > kXMax) kXMax=kompassX; } int16_t kompassY = getKompassY(); if (!kalibriert) { if (kompassY < kYMin) kYMin=kompassY; if (kompassY > kYMax) kYMax=kompassY; } int16_t kompassZ = getKompassZ(); if (!kalibriert) { if (kompassZ < kZMin) kZMin=kompassZ; if (kompassZ > kZMax) kZMax=kompassZ; } if (abs(kXMax - kXMin) < 3000 or abs(kYMax - kYMin) < 3000) { // Solange noch nicht kalibriert, drehenden Kreis anzeigen als Zeichen dafür // dass Nutzer einmal den Kompass drehen muss uint8_t dots[20] = {2,3,4,5, 14, 23, 31, 39, 47, 54, 61,60,59,58, 49, 40, 32, 24, 16, 9}; for (uint8_t w=0; w < 1; w++) { for (int i=0; i<20; i++) { uint8_t x=dots[i]%8; uint8_t y=dots[i]/8; uint8_t pic[8] = {0x00,0x18,0x18,0x7e,0x7e,0x18,0x18,0x00}; lightMatrix(pic,10); lightDot(x,y); delay(10); } } clearDots(); } else { int16_t kXBer = 0; int16_t kYBer = 0; int16_t kXMit = 0; int16_t kYMit = 0; if (!kalibriert) { // Anzahl Einheiten auf den Achsen kXBer = abs(abs(kXMax) - abs(kXMin)); kYBer = abs(abs(kYMax) - abs(kYMin)); } // Mitte der Achse if (!kalibriert) { kXMit = kXMin + (kXBer / 2.); kYMit = kYMin + (kYBer / 2.); } kalibriert = true; // gedachte Koordinaten float X = (float) (kompassX - kXMit) / (kXBer); float Y = (float) (kompassY - kYMit) / (kYBer); // Berechnung der Polarpeilung float heading = (atan2(X, Y)) * 180. / PI; if (heading < 0) heading += 360; String richt=" "; // "Umrechnung" in Richtungsangabe uint8_t pic[8] = {0,0,0,0,0,0,0,0}; if (heading >= 315+22.5 || heading < 0+22.5) { richt="N"; pic[0] = 0b11000011; pic[1] = 0b11100011; pic[2] = 0b11110011; pic[3] = 0b11011011; pic[4] = 0b11001111; pic[5] = 0b11000111; pic[6] = 0b11000011; pic[7] = 0b11000011; } else if(heading >= 0+22.5 && heading < 45+22.5){ richt="NO"; pic[0] = 0b00000000; pic[1] = 0b10010010; pic[2] = 0b10010101; pic[3] = 0b11010101; pic[4] = 0b10110101; pic[5] = 0b10010101; pic[6] = 0b10010010; pic[7] = 0b00000000; } else if(heading >= 45+22.5 && heading < 90+22.5) { richt="O"; pic[0] = 0b00111100; pic[1] = 0b01111110; pic[2] = 0b11100111; pic[3] = 0b11000011; pic[4] = 0b11000011; pic[5] = 0b11100111; pic[6] = 0b01111110; pic[7] = 0b00111100; } else if(heading >= 90+22.5 && heading < 135+22.5){ richt="SO"; pic[0] = 0b00000000; pic[1] = 0b01110010; pic[2] = 0b10000101; pic[3] = 0b01100101; pic[4] = 0b00010101; pic[5] = 0b00010101; pic[6] = 0b11100010; pic[7] = 0b00000000; } else if(heading >= 135+22.5 && heading < 180+22.5) { richt="S"; pic[0] = 0b01111111; pic[1] = 0b11111111; pic[2] = 0b11100000; pic[3] = 0b01111100; pic[4] = 0b00111110; pic[5] = 0b00000111; pic[6] = 0b11111111; pic[7] = 0b11111110; } else if(heading >= 180+22.5 && heading < 225+22.5) { richt="SW"; pic[0] = 0b00000000; pic[1] = 0b01101001; pic[2] = 0b10001001; pic[3] = 0b01001001; pic[4] = 0b00101111; pic[5] = 0b00101111; pic[6] = 0b11001001; pic[7] = 0b00000000; } else if(heading >= 225+22.5 && heading < 270+22.5) { richt="W"; pic[0] = 0b11000011; pic[1] = 0b11000011; pic[2] = 0b11000011; pic[3] = 0b11011011; pic[4] = 0b11111111; pic[5] = 0b11100111; pic[6] = 0b11000011; pic[7] = 0b11000011; } else if(heading >= 270+22.5 && heading < 315+22.5) { richt="NW"; pic[0] = 0b00000000; pic[1] = 0b11001001; pic[2] = 0b10101001; pic[3] = 0b10101001; pic[4] = 0b10101111; pic[5] = 0b10101111; pic[6] = 0b10101001; pic[7] = 0b00000000; } uint8_t dots[22] = {2,3,4,5, 14, 23, 31, 39, 47, 54, 61,60,59,58, 49, 40, 32, 24, 16, 9, 2,3}; // Leuchtpunkt, wo Norden ist int dnr = ((360-heading)/(360/20)); int dot=dots[dnr]; lightDot (dot%8,dot/8); delay (5); // und Himmelsrichtung in die Mitte lightMatrix (pic,3); } } } // ---------------------------------------------------------------------------------------------- void gaussmeter() { initKompass(); lightMatrix (good, 1000); while (1) { int16_t kompassX = getKompassX(); int16_t kompassY = getKompassY(); int16_t kompassZ = getKompassZ(); int32_t kompassGes = abs(kompassX) + abs(kompassY) + abs(kompassZ); // Magnetfeldstärke anzeigen int dots=kompassGes/100-40; if (dots > 64) dots=64; for (int w=1; w<20; w++) { for(int i=0;i<dots;i++) { lightDot(i%8,i/8); } delayMicroseconds(1); } clearDots(); } } // ------------------------------------------------ void thermometer() { initKompass(); lightMatrix (good, 1000); while (1) { int16_t temp = getTemp(); // Temperatur anzeigen int dots=round(temp); if (dots > 64) dots=64; for (int w=1; w<200; w++) { for(int i=0;i<dots;i++) { lightDot(i%8,i/8); } delayMicroseconds(100); } clearDots(); } } // ------------------------------------------------ void loop(void) { // Stromsparen - erst einmal auf Tastendruck warten // vllcht. hilft das gegen Sketch-Upload-Probleme clearDots(); while (digitalRead(PinTaster) == HIGH) { delay (100); } // und auf Loslassen warten while (digitalRead(PinTaster) == LOW) { delay (10); } // Hilfe-Scroller char *scrollText[8] = {
"..#.....#.#.#.........##.....###...#.#....##......#..#.............................#........##...........#............####....###.#............................", ".##.....#.#.#........#..#.....#..#.#.#......#.....#.#..............................#.#.....#..#.........#.#...........#........#..#............................", "#.#.....#.#.##...#......#.....#....#.##.....#.....##....#..####..##...##..##..##...#.#.....#.....##.#.#.#.#.####......#........#..##...#...#.####...#..####....", "..#.....#.#.#.#.#......#......#..#.#.#....##......##...#.#.#.#.#.#.#.#.#.#...#.....####....#.##.#.#.#.#.##..#.#.#.....###......#..#.#.#.#.#..#.#.#.#.#.#.#.#...", "..#.....#.#.#.#.#.....#.......#..#.#.#......#.....#.#..#.#.#.#.#.#.#.#.#..#...#......#.....#..#.#.#.#.#.#.#.#.#.#........#.....#..#.#.###.#..#.#.#.#.#.#.#.#...", "..#.##..#.#.#.#.#....#....##..#..#.#.#......#.##..#..#.#.#.#.#.#.#.#.#.#...#...#.....#..##.#..#.#.#.#.#.#.#.#.#.#........#.##..#..#.#.#...#..#.#.#.#.#.#.#.#...", "..#.##...#..#.#.#....####.##..#..#.#..#...##..##..#..#..#..#.#.#.##...##.##..##......#..##..##...##..##.##..#.#.#.#...###..##..#..#.#..##.#..#.#.#..#..#.#.#.#.", ".................................................................#......................................#......................................................"
}; while (digitalRead(PinTaster) == HIGH) { scroll (scrollText,160,75); } while (digitalRead(PinTaster) == LOW) { delay(10); } // Funktion auswählen // 1 = Nerd-Uhr int funcAnz = 5; while (digitalRead(PinTaster) == HIGH) { for (uint8_t i=0;i<funcAnz;i++) { lightDot(i%8,i/8); delay(25); // if (digitalRead(PinTaster) == LOW) break; } } int f=-1; while (1) { long ms=waitTaster(60000); if (ms > 500) { break; } else { f++; if (f>=funcAnz) f=0; while (digitalRead(PinTaster) == HIGH) { for (uint8_t i=0;i<=f;i++) { lightDot(i%8,i/8); } } } } f++; switch (f) { case 1: { nerdUhr(); break; } case 2: { tilt(); break; } case 3: { kompass(); break; } case 4: { gaussmeter(); break; } case 5: { thermometer(); break; } } }