Zum Inhalt

Graviertes RGB-Schild

Vor zwei Jahren habe ich mir eine T8 CNC aus China besorgt. Diese eignet sich sehr gut um ein Plexiglas zu beschriften. Nun braucht man neben dem gravierten Plexiglas nur noch eine Möglichkeit dieses zu illuminieren. Darauf hin ist dieses Projekt entstanden. =)

Ales erstes habe ich es natürlich mit meinem Namen versucht. Als zweites, für ein Geschenk ein Bibeltext. Diesen zweiten Versuch möchte ich hier auch zeigen, da er mir sehr gefällt.

Hier mal ein Video:

Aufbau

Das RGB-Schild besteht aus: - Arduino Nano - Plexiglasscheibe (ich habe meine vom Conrad) - 3D-gedruckter Sockel - WS2812B-LED-Streifen - USB-Kabel zur Versorgung

Bilder


fertiges Schild
verwendete Graviermaschine
Platine und LED-Streifen ist eingeklebt
von hinten gravierter Text

Gravieren

Die Vektordaten für die Gravur habe ich mit Inkscape erzeugt. Das Problem ist allerdings, das die üblichen Vektorschriften nur bedingt nutzbar sind. Man sollte eine Schriftart haben, die nur aus einer Linie besteht. Eine Gravurschriftart.

Für Inkscape gibt es hier das Plugin Hershey Text.

Die mit Inkscape erzeugte SVG-Datei habe ich anschließend in Estlcam zu einer NC-Datei verarbeitet, welche ich mit meiner Gravurmaschine nutzen kann.

Wichtig ist natürlich das Objekt vor dem Konvertieren zu spiegeln, damit man den Text "von hinten" eingravieren kann.

Nachdem das Schild graviert ist und die Aussparung für das Schild im 3D-Druck mit einer Nagelfeile auf die richtige Größe gebracht wurde, kann man es einkleben.

Anschluss

Der LED-Streifen wird an VCC und GND vom Arduino angeschlossen.

Der Daten-Pin des Streifens an D2 des Arduinos.

Software

Als externe Bibliothek wird RGBConverter und Adafruit NeoPixel benötigt.

Die Software spielt eine RGB-Animation ab (funktioniert ohne PC). Die LEDs können aber auch über entsprechende Befehle vom PC aus gesteuert werden.

led_schild.ino

#include <RGBConverter.h>
#include <Adafruit_NeoPixel.h>
#include <EEPROM.h>

Adafruit_NeoPixel pixels = Adafruit_NeoPixel(5, 2, NEO_GRB + NEO_KHZ800);

String serialBuffer = "";
byte pixelBuffer[5][3];

bool animation_active = true;

void setup() {
  pixels.begin();
  pixels.show();

  Serial.begin(115200);

  eepromRead();
}

void loop() {
  //Serial-Bsp.: s;255;000;000;255;000;000;255;000;000;255;000;000;255;000;000
  decodeSerial();
  if(animation_active) animation();
}





unsigned long time_last;
double colordistance = 0.10;
double counter = 0.0;
void animation() {
  unsigned long time = millis();
  if(time-time_last>5) {
    for(int i = 0; i<5; i++) {
      double h = counter + colordistance * (i+1);
      if(h>1.0) h -= 1.0;

      byte rgb[3];
      RGBConverter conv;
      conv.hslToRgb(h, 1.0, 0.30, rgb);

      for(int j = 0; j<3; j++) {
        pixelBuffer[i][j] = rgb[j];
      }

      if(counter < 1.0) counter+=0.0003; else counter = 0.0;
    }

    showPixel();

    time_last = time;
  }
}

void showPixel() {
  for(int i = 0;i<5;i++) {
    pixels.setPixelColor(i, pixels.Color(pixelBuffer[i][0],pixelBuffer[i][1],pixelBuffer[i][2]));
  }

  pixels.show();
}

void decodeSerial() {
  if (Serial.available() > 0) {
    char incomingByte = Serial.read();

    if(incomingByte!='\n') {
      serialBuffer += incomingByte;
    } else {
      if(serialBuffer[0]=='e' && serialBuffer.length()==1) {
        eepromSave();
      }
      if(serialBuffer[0]=='s' && serialBuffer.length()==61) {

        bool seperatorCheck = true;
        int p = 1;
        while(p<=57) {
          if(serialBuffer[p]!=';') seperatorCheck = false;
          p += 4;
        }

        if(seperatorCheck) {
          animation_active = false;

          int pos = 2;
          for(int i = 0;i<5;i++) {
            for(int j = 0;j<3;j++) {
              pixelBuffer[i][j] = serialBuffer.substring(pos,pos+2).toInt();
              pos+=4;
            }
          }

          showPixel();
        }

      }

      serialBuffer = "";
    }
  }
}

void eepromSave() {
  for(int i = 0;i<5;i++) {
    EEPROM.update(i*3 + 0, pixelBuffer[i][0]);
    EEPROM.update(i*3 + 1, pixelBuffer[i][1]);
    EEPROM.update(i*3 + 2, pixelBuffer[i][1]);
  }
}

void eepromRead() {
  for(int i = 0;i<5;i++) {
    pixelBuffer[i][0] = EEPROM.read(i*3 + 0);
    pixelBuffer[i][1] = EEPROM.read(i*3 + 1);
    pixelBuffer[i][2] = EEPROM.read(i*3 + 2);
  }

  showPixel();
}

Und hier noch eine Alternative für ein gelbes Bild mit leichtem und sanftem Helligkeits/Farbflackern. Benutzt die CircularBuffer-Library. Ist durch die vielen Buffer relativ ressourcenhungrig umgesetzt. Aber funktioniert.

led_schild.ino - Farbflackern

#include <RGBConverter.h>
#include <Adafruit_NeoPixel.h>
#include <CircularBuffer.h>
#include <EEPROM.h>

Adafruit_NeoPixel pixels = Adafruit_NeoPixel(5, 2, NEO_GRB + NEO_KHZ800);

String serialBuffer = "";
byte pixelBuffer[5][3];

bool animation_active = true;

void setup() {
  pixels.begin();
  pixels.show();

  Serial.begin(115200);

  eepromRead();
}

void loop() {
  //Serial-Bsp.: s;255;000;000;255;000;000;255;000;000;255;000;000;255;000;000
  decodeSerial();
  if(animation_active) animation();
}





unsigned long time_last;
CircularBuffer<float, 25> history_buffer[5][3];
void animation() {
  unsigned long time = millis();
  if(time-time_last>20) {
    for(int i = 0; i<5; i++) {
      byte rgb[3];
      RGBConverter conv;

      float h = 0.00 + (random(300)/1000.0);
      float s = 1.0;
      float l = 0.0 + (random(800)/1000.0);

      history_buffer[i][0].push(h);
      history_buffer[i][1].push(s);
      history_buffer[i][2].push(l);

      float mean[] = { 0, 0, 0 };
      for(int j = 0; j<3; j++) {
        float sum = 0.0;
        for(uint8_t k = 0; k < history_buffer[i][j].size()-1; k++) { 
          sum += history_buffer[i][j][k];
        }
        mean[j] = sum / history_buffer[i][j].size();
      }

      conv.hslToRgb(mean[0], mean[1], mean[2], rgb);

      for(int j = 0; j<3; j++) {
        pixelBuffer[i][j] = rgb[j];
      }

    }

    showPixel();

    time_last = time;
  }
}

void showPixel() {
  for(int i = 0;i<5;i++) {
    pixels.setPixelColor(i, pixels.Color(pixelBuffer[i][0],pixelBuffer[i][1],pixelBuffer[i][2]));
  }

  pixels.show();
}

void decodeSerial() {
  if (Serial.available() > 0) {
    char incomingByte = Serial.read();

    if(incomingByte!='\n') {
      serialBuffer += incomingByte;
    } else {
      if(serialBuffer[0]=='e' && serialBuffer.length()==1) {
        eepromSave();
      }
      if(serialBuffer[0]=='s' && serialBuffer.length()==61) {

        bool seperatorCheck = true;
        int p = 1;
        while(p<=57) {
          if(serialBuffer[p]!=';') seperatorCheck = false;
          p += 4;
        }

        if(seperatorCheck) {
          animation_active = false;

          int pos = 2;
          for(int i = 0;i<5;i++) {
            for(int j = 0;j<3;j++) {
              pixelBuffer[i][j] = serialBuffer.substring(pos,pos+2).toInt();
              pos+=4;
            }
          }

          showPixel();
        }

      }

      serialBuffer = "";
    }
  }
}

void eepromSave() {
  for(int i = 0;i<5;i++) {
    EEPROM.update(i*3 + 0, pixelBuffer[i][0]);
    EEPROM.update(i*3 + 1, pixelBuffer[i][1]);
    EEPROM.update(i*3 + 2, pixelBuffer[i][1]);
  }
}

void eepromRead() {
  for(int i = 0;i<5;i++) {
    pixelBuffer[i][0] = EEPROM.read(i*3 + 0);
    pixelBuffer[i][1] = EEPROM.read(i*3 + 1);
    pixelBuffer[i][2] = EEPROM.read(i*3 + 2);
  }

  showPixel();
}

3D Modell

🧊 Sockel

📅 23.07.2020