Magnetfeld Drehlicht

Deko Lampe mit Magnet Sensor

Ich wollte eigentlich immer schon ein Moodlight haben, aber die sind mir meist zu spießig und zu sehr so Spa-Chillout-mäßig 😃 Deshalb hatte ich überlegt, dass man im Prinzip ein analogeres Licht mischen könnte, indem einfach drei Lampen in den Grundfarben entsprechend Additiv geschaltet werden. Das hat auch den nötigen RGB-Retro-Bezug... Ich möchte auch, dass man die Lampen sieht, also die Glühbirnen, denn das typische Moodlight ist ja eher so eine gleichmäßige langweilige Farbwand... Dann fehlt dazu nur noch der richtige Schalter für die einzelnen Lampen. Am besten etwas, was in diesem Zusammenhang möglichst ungewöhnlich ist und was man vielleicht nicht anfassen kann, aber was trotzdem intuitiv ist und zum Rumspielen einlädt! 😳 ...ein Kompass. Genau. Ich baue ein Drehlicht, das auf das Erdmagnetfeld reagiert.

Material

Eigentlich habe ich wieder wie immer alles bei Amazon bekommen – bis auf eine Ausnahme – für das Gehäuse nutze ich dieses Mal Glas. Dazu nehme ich einfach, was so rumliegt. Glücklicherweise bin ich immer zu faul, das Altglas wegzuschaffen, insofern habe ich freie Wahl bei der Suche nach einem geeigneten Schraubglas. 😃 Insgesamt summiert sich der Materialwert für das Drehlicht dann auf ca. 50 EUR.

Vorbereitung

Zuerst teste ich wieder die einzelnen Kernbestandteile. In diesem Fall gibt es eigentlich bloß ein externes Shield, nämlich den Magnetometer, also den Kompass. Die Verkabelung ist sehr einfach, wie bei allen digitalen Signalen gibt es nur SCL und SDA sowie VCC und GND für die 5V Stromversorgung. Bei Adafruit gibt es auch schon ein Tutorial zu diesem Sensor dazu. Hier geht es um ein Adafruit eigenes Shield, dieses nutzt aber den gleichen Magnetometer Chip wie die günstigen Shields auf Amazon (HMC5883L). Das Codebeispiel kann man eigentlich 1:1 benutzen, ich habe nur sehr wenige Veränderungen gemacht und hauptsächlich ausgemistet. Ich teste schon mal mit ein paar bunten LEDs, die Gradzahl vom Sensor teile ich einfach in sechs Teile und nutze eine switch-Anweisung um die jeweiligen Lampen entsprechend des Drehwinkels zu schalten, mehr dazu im finalen Code... Zum Testen stecke ich die LEDs einfach ohne Widerstand und schalte die entsprechenden Pins auf HIGH.

#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_HMC5883_U.h>

/* Assign a unique ID to this sensor at the same time */
Adafruit_HMC5883_Unified mag = Adafruit_HMC5883_Unified(12345);

void displaySensorDetails(void)
{
  sensor_t sensor;
  mag.getSensor(&sensor);
  Serial.println("------------------------------------");
  Serial.print  ("Sensor:       "); Serial.println(sensor.name);
  Serial.print  ("Driver Ver:   "); Serial.println(sensor.version);
  Serial.print  ("Unique ID:    "); Serial.println(sensor.sensor_id);
  Serial.print  ("Max Value:    "); Serial.print(sensor.max_value); Serial.println(" uT");
  Serial.print  ("Min Value:    "); Serial.print(sensor.min_value); Serial.println(" uT");
  Serial.print  ("Resolution:   "); Serial.print(sensor.resolution); Serial.println(" uT");
  Serial.println("------------------------------------");
  Serial.println("");
  delay(500);
}

void setup(void)
{
  Serial.begin(9600);
  Serial.println("HMC5883 Magnetometer Test"); Serial.println("");

  /* Initialise the sensor */
  if(!mag.begin())
  {
    /* There was a problem detecting the HMC5883 ... check your connections */
    Serial.println("Ooops, no HMC5883 detected ... Check your wiring!");
    while(1);
  }

  /* Display some basic information on this sensor */
  displaySensorDetails();
}

void loop(void)
{
  /* Get a new sensor event */
  sensors_event_t event;
  mag.getEvent(&event);

  /* Display the results (magnetic vector values are in micro-Tesla (uT)) */
  Serial.print("X: "); Serial.print(event.magnetic.x); Serial.print("  ");
  Serial.print("Y: "); Serial.print(event.magnetic.y); Serial.print("  ");
  Serial.print("Z: "); Serial.print(event.magnetic.z); Serial.print("  ");Serial.print("uT");

  // Hold the module so that Z is pointing 'up' and you can measure the heading with x&y
  // Calculate heading when the magnetometer is level, then correct for signs of axis.
  float heading = atan2(event.magnetic.y, event.magnetic.x);
  float declinationAngle = 0.0523599;
  heading += declinationAngle;

  // Correct for when signs are reversed.
  if(heading < 0)
    heading += 2*PI;

  // Check for wrap due to addition of declination.
  if(heading > 2*PI)
    heading -= 2*PI;

  // Convert radians to degrees for readability.
  float headingDegrees = heading * 180/M_PI;

  Serial.print(" Heading (degrees): "); Serial.println(headingDegrees);

  delay(100);
}

Arduino Sketch testen

Nun geht es darum, die LEDs durch richtige Lampen zu ersetzen – das LED-Licht ist mir zu kühl, und ich mag es, wenn man einen Glühdraht sieht. Deswegen nehme ich kleine Lampen, wie sie bei vielen Autos in den Leuchten verbaut sind. Die Größe hier ist E10. Die verbrauchen nicht allzu viel, aber immerhin zu viel für den Arduino. Ich dachte eigentlich: "Naja, steckst du die wie die LEDs direkt an die Pins ran", aber das zwingt den Arduino sofort in die Knie. Da ich die Lampen also nicht direkt schalten kann, brauche ich etwas dazwischen – eine Art Schalter, der mit einer kleinen Spannung vom Pin eine größere für die Lampen schalten kann. Zeit, sich mal mit Transistoren zu beschäftigen und das ordentlich zu machen!

Transistoren mit Arduino testen

Also habe ich kurzerhand welche von denen bestellt und mal an einer Lampenschaltung getestet. Wichtig zu wissen ist, dass ein Transistor offenbar prinzipiell die Minus-Leistung unterbricht und nicht die Plus-Leitung. Der Transistor kommt also an das schwarze Ground-Kabel nach der Lampe und bevor es wieder ins Netz mündet. Dann gibt es noch zwei verschiedene Versionen von Transistoren, für meinen Fall kommt NPN in Frage. Und anscheinend gibt es noch verschiedene Stromstärken – ich hatte auch nach einigen Versuchen noch andere Transistoren bestellt und getestet, aber letztendlich fahre ich hier mit 0,1A ganz gut, warum auch immer... Um das richtig zu testen, habe ich dann die Kabel schon mal an die Lampensockel gelötet, das war ein Riesenwirrwarr. Bis hierhin klappt alles ganz gut.

Fahrradlampen mit Transistoren schalten

Prototyp

Für den Prototypen baue ich alles auf den Adafruit Trinket um. Zum Testen nehme ich immer den größeren Arduino Uno, denn damit hantiert sich leichter. Für das Drehlicht soll der Microcontroller aber so klein wie möglich sein, und der Trinket kann ich voll ausreizen, dabei ist er noch super klein und hübsch. Beim Umbau ist eigentlich nur zu beachten, dass die Pins beim Trinket jeweils mehrere Belegungen haben können, aber das erklärt das Pinout-Diagramm ganz gut. Wichtig dabei ist die Verteilung von SDA und SCL. Ich finde das fast ein bisschen blöd, dass sie nicht nebeneinander liegen, aber was soll's. Die restlichen Pins gehen eigentlich fast alle für die Lampenschaltung und den Sensor drauf. Auch der Code muss leicht angepasst werden, insbesondere was die Includes angeht, da der Trinket anscheinend etwas anders tickt. Aber den kompletten Code gibt es unten, hier gibt es auch keine Möglichkeit, mit Serial etwas zu loggen, usw. Der Code nutzt letztendlich auch fast den ganzen Trinket-Speicher aus. Damit habe ich auch wieder meine Pin-Tabelle, um nachher alles richtig löten zu können.

trinket GND BAT 5V #0 #1 #2 #3 #4
kompass GND VCC SDA SCL
power GND PWR
transis B B B

Arduino Trinket Prototyp mit Transistoren

#include <TinyWireM.h>
#include <USI_TWI_Master.h>

#include <Adafruit_HMC5883_U.h>
Adafruit_HMC5883_Unified mag = Adafruit_HMC5883_Unified(12345);
#define r 1
#define g 3
#define b 4

void setup(void)
{
  mag.begin();
  pinMode(r,OUTPUT);
  pinMode(g,OUTPUT);
  pinMode(b,OUTPUT);
}

void loop(void)
{

  sensors_event_t event;
  mag.getEvent(&event);
  float heading = atan2(event.magnetic.y, event.magnetic.x);
  if(heading < 0) heading += 2*PI;
  if(heading > 2*PI) heading -= 2*PI;
  int degrees = heading*180/M_PI+1;

  switch (degrees/60) {
    case 1:
      digitalWrite(r,HIGH);
      digitalWrite(g,HIGH);
      digitalWrite(b,LOW);
      break;
    case 2:
      digitalWrite(r,LOW);
      digitalWrite(g,HIGH);
      digitalWrite(b,LOW);
      break;
    case 3:
      digitalWrite(r,LOW);
      digitalWrite(g,HIGH);
      digitalWrite(b,HIGH);
      break;
    case 4:
      digitalWrite(r,LOW);
      digitalWrite(g,LOW);
      digitalWrite(b,HIGH);
      break;
    case 5:
      digitalWrite(r,HIGH);
      digitalWrite(g,LOW);
      digitalWrite(b,HIGH);
      break;
    case 6:
      digitalWrite(r,HIGH);
      digitalWrite(g,LOW);
      digitalWrite(b,LOW);
      break;
    case 0:
      digitalWrite(r,HIGH);
      digitalWrite(g,LOW);
      digitalWrite(b,LOW);
      break;
  }

}

Fertigung

Arduino Glaslampe mit Fahrradlämpchen

Jetzt fehlt nur noch alles zusammenzulöten und zu verkleben. Die Lampenfassungen und Transistoren hatte ich beim Testen schon verlötet. Bleibt also nur noch alles an den Trinket und den Sensor zu löten und die Lampensockel mit Heißkleber auf den Schraubdeckel zu kleben. Ich war mir nicht ganz sicher – die Unterseiten von solchen Deckeln scheinen ja irgendeine Art Beschichtung zu haben, aber ich habe dann doch vorsichtshalber erst einen Heißklebertropfen geklebt, den aushärten lassen und dann den Sockel darauf festgeklebt. Damit haben die Sockel genug Abstand vom Metalldeckel. Die ganzen Kabel, der Trinket sowie der Sensor hängen dann alle frei im Glas. Ich mag das, wenn man die Kabel sieht 😃

Test vom Drehlicht

Okay, das wäre es soweit zum Gehäuse, an sich ist es jetzt fertig. Funktioniert aber nur so halb 😳. Ich habe dann ein wenig rumprobiert und mal die Kühlschrankmagneten drangehalten, aber irgendwie scheint der Kompass falsche Signale zu geben, die Lampen schalten beim Drehen nicht so, wie sie sollten. Ich kann mir das nur durch störende Magnetfelder durch die ganze Verkabelung erklären. Ich habe dann den Sensor mal nach unten ins Glas verlegt, wo es keine Kabel gibt. Siehe da – looft 👍

Der Gyrosensor als Farbeinstellung

Zuletzt schraube ich noch die bunten Birnen rein. Mal sehen, ob die ohne Vorwiderstand eine Weile halten, ansonsten muss ich da nochmal etwas feintunen. Ich denke auch, dass es gut so ist, dass das Drehlicht nur auf 6 Farben schaltet. Das unterstreicht zum einen den analogen Charakter, den ich so mag, und andererseits könnte man theoretisch die Lampen noch mit PWM dimmen, um fließende Farbübergänge zu erzielen. Aber ob die Lampen das auf Dauer mitmachen würden, bezweifle ich, und ich denke auch, dass dabei das haptische Feedback geringer wäre als mit festen Abstufungen... 😃 Jetzt beim ersten richtigen Test stellt sich auch heraus, dass das Licht ganz anders gestreut wird, als ich erwartet hatte. Durch die Form vom Glas entstehen interessante Lichtbrechungen. Die waren in keinster Weise beabsichtigt, aber ich mag sie!

Arduino und Gyro Sensor im Glas als Deko Lampe

Dummerweise fängt dort irgendwas an zu schmoren, ich glaube, ich habe da etwas falsch gemacht 💩. Und ehe mir noch die Hütte abbrennt... 😳 Das ist mir zu blöd. Ich stelle jetzt um auf LED, da brauche ich mir keinen Kopf mehr machen, welche Bauteile wie zusammenpassen. Zum Glück habe ich noch von meinem Farbdingens einen digitalen LED-Streifen über. Muhahahah 😃 Den Trinket muss ich aber leider wieder rausschneiden und durch einen normalen Nano ersetzen, denn der Trinket ist zu klein für die zusätzliche Library für den LED-Streifen 😳.

#include "LPD8806.h"
#include "SPI.h"
int nLEDs = 48;
int clockPin = 2;
int dataPin  = 3;
LPD8806 strip = LPD8806(nLEDs, dataPin, clockPin);

#include <Adafruit_HMC5883_U.h>
Adafruit_HMC5883_Unified mag = Adafruit_HMC5883_Unified(12345);

// gamma table
const uint8_t PROGMEM gamma[] = {
    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  1,  1,  1,  1,
    1,  1,  1,  1,  1,  1,  1,  1,  1,  2,  2,  2,  2,  2,  2,  2,
    2,  3,  3,  3,  3,  3,  3,  3,  4,  4,  4,  4,  4,  5,  5,  5,
    5,  6,  6,  6,  6,  7,  7,  7,  7,  8,  8,  8,  9,  9,  9, 10,
   10, 10, 11, 11, 11, 12, 12, 13, 13, 13, 14, 14, 15, 15, 16, 16,
   17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, 24, 24, 25,
   25, 26, 27, 27, 28, 29, 29, 30, 31, 32, 32, 33, 34, 35, 35, 36,
   37, 38, 39, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 50,
   51, 52, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 66, 67, 68,
   69, 70, 72, 73, 74, 75, 77, 78, 79, 81, 82, 83, 85, 86, 87, 89,
   90, 92, 93, 95, 96, 98, 99,101,102,104,105,107,109,110,112,114,
  115,117,119,120,122,124,126,127,129,131,133,135,137,138,140,142,
  144,146,148,150,152,154,156,158,160,162,164,167,169,171,173,175,
  177,180,182,184,186,189,191,193,196,198,200,203,205,208,210,213,
  215,218,220,223,225,228,231,233,236,239,241,244,247,249,252,255
};

void setup(void)
{
  mag.begin();
  strip.begin();
  strip.show();
  Serial.begin(9600);
}

void loop(void)
{

  sensors_event_t event;
  mag.getEvent(&event);
  float heading = atan2(event.magnetic.y, event.magnetic.x);
  if(heading < 0) heading += 2*PI;
  if(heading > 2*PI) heading -= 2*PI;
  int degrees = heading*180/M_PI+1;

  Serial.println(degrees/30);
  switch (degrees/30) {
    case 1:  fullcolor(255,0,0); break;
    case 2:  fullcolor(255,128,0); break;
    case 3:  fullcolor(255,255,0); break;
    case 4:  fullcolor(150,255,0); break;
    case 5:  fullcolor(0,255,0); break;
    case 6:  fullcolor(0,255,128); break;
    case 7:  fullcolor(0,255,255); break;
    case 8:  fullcolor(0,128,255); break;
    case 9:  fullcolor(0,0,255); break;
    case 10: fullcolor(128,0,255); break;
    case 11: fullcolor(255,0,255); break;
    case 12: fullcolor(255,0,128); break;
    case 0:  fullcolor(255,0,128); break;
  }

  delay(100);

}

// fill entire strip with
void fullcolor(int r, int g, int b) {

  //gamma
  r = pgm_read_byte(&gamma[r]);
  g = pgm_read_byte(&gamma[g]);
  b = pgm_read_byte(&gamma[b]);

  int i;
  for (i=0; i < nLEDs; i++) {
    uint32_t color = strip.Color(r,b,g);
    strip.setPixelColor(i, color);
  }
  strip.show();

}

Den Code stelle ich lediglich insofern um, dass nun keine drei einzelnen Pins zusammengeschaltet werden, sondern ein RGB-Signal an den Streifen geschickt wird. Dadurch kann ich auch mehr Zwischentöne mischen, und die Farben werden sauberer. Apropos Farben, hier braucht es noch eine Gammakorrektur, sonst werden die Farben beim Mischen matschig. Aber auch das ist schnell implementiert. Endlich fertig 👍. Der Effekt lohnt das Gemehre 😃.