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);). Damit ist alles erledigt und die Verbindung kann mit endTransmission(); wieder 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:
- VIN --> nicht angeschlossen
- 3V3 --> Arduino 3.3V
- GND --> Arduino Masse
- SCL --> Arduino A5
- SDA --> Arduino A4
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
Der Source-Code
8x8-acc-mag.c (klicken, um diesen Abschnitt aufzuklappen)
////////////////////////////////////////////////////////
// (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; }
}
}