Innehållsförteckning:

Trä LED -klocka - Analog stil: 11 steg (med bilder)
Trä LED -klocka - Analog stil: 11 steg (med bilder)

Video: Trä LED -klocka - Analog stil: 11 steg (med bilder)

Video: Trä LED -klocka - Analog stil: 11 steg (med bilder)
Video: Моя работа наблюдать за лесом и здесь происходит что-то странное 2024, November
Anonim
Trä LED -klocka - Analog stil
Trä LED -klocka - Analog stil

Det är en analog LED -klocka i trä. Jag vet inte varför jag inte har sett en av dessa tidigare.. trots att de digitala typerna är väldigt vanliga. Anyhoo, här kör vi!

Steg 1:

Bild
Bild
Bild
Bild

Plywood -klockprojektet startade som ett enkelt startprojekt för CNC -routern. Jag tittade på enkla projekt online och hittade den här lampan (bilden ovan). Jag hade också sett digitala klockor som lyser genom träfaner (bilden ovan). Så att kombinera de två projekten var en självklar idé. För att utmana mig själv bestämde jag mig för att inte använda faner utan bara en bit trä för detta projekt.

Steg 2: Design

Design
Design
Design
Design

Jag utformade klockan i Inkscape (bilden ovan). Designen är mycket enkel efter val. Jag bestämde mig för att dra spår för ledningarna eftersom jag vid denna tidpunkt var osäker på om jag ville gå med radiella eller perimeterledningar. (Jag bestämde mig för att slutligen gå med omkretsledningar.) En neopixel går in i vart och ett av de små cirkulära hålen för att visa minut- och timtid, med fem minuters precision. Cirkeln i mitten dirigeras ut för att rymma elektroniken.

Steg 3: CNC -bearbetning

CNC -bearbetning
CNC -bearbetning
CNC -bearbetning
CNC -bearbetning
CNC -bearbetning
CNC -bearbetning
CNC -bearbetning
CNC -bearbetning

Jag konstruerade verktygsbanorna på MasterCAM och använde en technoRouter för att fräsa ur klockan från 3/4 tum plywood. Jag använder en 15 "x15" bit för detta, med minimalt slöseri. Tricket är att ta bort så mycket av träet som möjligt utan att bryta igenom träet. Att lämna 0,05 "-0,1" är ett bra val för ljust trä. Om du är osäker är det bättre att lämna mer trä, eftersom du alltid kan slipa det andra ansiktet. Jag slutade med att ta bort lite för mycket virke från vissa delar, men tack och lov lider resultaten inte för mycket på grund av detta.

Anmärkning för användare utan tillgång till en CNC:

Detta projekt kan enkelt utföras med en borrpress. Du behöver bara ställa in stoppet vid en punkt där du lämnar cirka 0,1 trä kvar vid basen. Du måste vara exakt, men inte för exakt. När allt kommer omkring är det idealiskt att ingen kommer att se alla lysdioder lysa vid samtidigt, så att du kan komma undan med en liten slop.

Steg 4: Elektronik

Elektronik
Elektronik
Elektronik
Elektronik
Elektronik
Elektronik

Elektroniken är ganska enkel. Det finns 24 neopixlar, tolv för visning av timmar och tolv för visning av minuter, med fem minuters precision. En Arduino pro mini styr neopixlarna och den får exakt tid genom en DS3231 realtidsklocka (RTC) -modul. RTC -modulen har en myntcell som backup, så den förlorar inte tid även när strömmen är avstängd.

Material:

Arduino pro mini (eller någon annan Arduino för den delen)

DS3231 breakout board

Neopixels i individuella breakout boards

Steg 5: Elektronikmontering

Elektronikmontering
Elektronikmontering
Elektronikmontering
Elektronikmontering
Elektronikmontering
Elektronikmontering
Elektronikmontering
Elektronikmontering

Jag kopplade neopixlarna i en sträng med 2,5 trådar för de första tolv lysdioderna och fyra-tums tråd för de kommande tolv. Jag kunde ha använt lite mindre trådlängder. Efter att ha gjort strängen testade jag det och såg till att lödet lederna var bra. Jag lade till en tillfällig strömbrytare för att slå på alla lysdioder, bara för att visa upp mig.

Steg 6: Torrkörning

Torrkörning
Torrkörning
Torrkörning
Torrkörning
Torrkörning
Torrkörning
Torrkörning
Torrkörning

Efter att ha experimenterat, satt lysdioder i hålen och tänt dem alla var jag nöjd med resultaten. Så jag slipade framsidan lite och applicerade en PU -kappa. Det slutade med att jag slipade av kappan senare, men det är en bra idé att låta den vara på om du inte tycker att den är estetiskt obehaglig.

Steg 7: Epoxi

Epoxi
Epoxi
Epoxi
Epoxi

Efter några tester med ledpositionen i hålen, tänkte jag att den bästa diskussionen uppnås när lysdioderna är cirka 0,2 från hålets ände. När du försöker detta själv kommer lysdiodernas ljusstyrka att vara mycket olika varje hål. Oroa dig inte för det här; vi fixar det i kod. Detta beror på den typ av borr som jag använde. Om jag skulle göra detta igen skulle jag använda en borrkrona för hålen. Men i alla fall för att få avståndet blandade jag lite epoxi och la lite i varje hål.

Steg 8: Sätta ihop allt

Få alltid att falla på plats
Få alltid att falla på plats
Få alltid att falla på plats
Få alltid att falla på plats
Få alltid att falla på plats
Få alltid att falla på plats
Få alltid att falla på plats
Få alltid att falla på plats

Lysdioderna kommer att placeras från klockans 12-timmars position som rör sig moturs genom alla timvisare och sedan till minutvisaren, återigen från 60-minutersmarkeringen som rör sig moturs. Detta är så att när vi ser framifrån lyser LED -mönstret medurs.

Efter att epoxin härdat i en timme lade jag i lite mer epoxi. Den här gången placerade jag lysdioderna i hålen och såg till att täcka ledningarna och lödfogarna med epoxi. Detta ger god ljusdiffusion och säkrar trådarna.

Steg 9: Kod

Koda
Koda

Koden finns på GitHub, ändra den gärna för din användning. När du slår på alla lysdioder till samma nivå kommer ljusstyrkan som lyser igenom att vara väldigt olika i varje hål. Detta beror på den olika tjockleken på trä i hålen och skillnaden i träets nyans. Som du kan se varierar träfärgen ganska mycket i min bit. För att åtgärda denna skillnad i ljusstyrka gjorde jag en matris av ledda ljusstyrkanivåer. Och minskade ljusstyrkan hos de ljusare lysdioderna. Det är en test- och felprocess och kan ta flera minuter, men resultaten är väl värda det.

plywoodClock.ino

// Plywood klocka
// Författare: tinkrmind
// Attribution 4.0 International (CC BY 4.0). Du är fri att:
// Dela - kopiera och omfördela materialet i valfritt medium eller format
// Anpassa - remixa, transformera och bygga vidare på materialet för alla ändamål, även kommersiellt.
// Hurra!
#omfatta
#inkludera "RTClib.h"
RTC_DS3231 rtc;
#inkludera "Adafruit_NeoPixel.h"
#ifdef _AVR_
#omfatta
#endif
#definieraPIN6
Adafruit_NeoPixel strip = Adafruit_NeoPixel (60, PIN, NEO_GRB + NEO_KHZ800);
int hourPixel = 0;
int minutPixel = 0;
unsignedlong lastRtcCheck;
String inputString = ""; // en sträng för att hålla inkommande data
boolean stringComplete = false; // om strängen är komplett
int -nivå [24] = {31, 51, 37, 64, 50, 224, 64, 102, 95, 255, 49, 44, 65, 230, 80, 77, 102, 87, 149, 192, 67, 109, 68, 77};
voidsetup () {
#ifndef ESP8266
medan (! Seriell); // för Leonardo/Micro/Zero
#endif
// Detta är för Trinket 5V 16MHz, du kan ta bort dessa tre rader om du inte använder en prydnad
#if definierat (_AVR_ATtiny85_)
om (F_CPU == 16000000) clock_prescale_set (clock_div_1);
#endif
// Slut på prydnadsspecifik kod
Serial.begin (9600);
strip.begin ();
strip.show (); // Initiera alla pixlar till 'av'
om (! rtc.begin ()) {
Serial.println ("Det gick inte att hitta RTC");
medan (1);
}
pinMode (2, INPUT_PULLUP);
// rtc.adjust (DateTime (F (_ DATE_), F (_ TIME_)));
if (rtc.lostPower ()) {
Serial.println ("RTC förlorade ström, låt oss ställa in tiden!");
// följande rad anger RTC till datum och tid då denna skiss sammanställdes
rtc.adjust (DateTime (F (_ DATE_), F (_ TIME_)));
// Denna rad anger RTC med ett tydligt datum och tid, till exempel att ställa in
// 21 januari 2014 klockan 03.00 skulle du ringa:
// rtc.adjust (DateTime (2017, 11, 06, 2, 49, 0));
}
// rtc.adjust (DateTime (2017, 11, 06, 2, 49, 0));
// lightUpEven ();
// medan (1);
lastRtcCheck = 0;
}
voidloop () {
if (millis () - lastRtcCheck> 2000) {
DateTime nu = rtc.now ();
Serial.print (nu. Timme (), DEC);
Serial.print (':');
Serial.print (nu. Minut (), DEC);
Serial.print (':');
Serial.print (nu. Sekund (), DEC);
Serial.println ();
showTime ();
lastRtcCheck = millis ();
}
if (! digitalRead (2)) {
lightUpEven ();
}
if (stringComplete) {
Serial.println (inputString);
if (inputString [0] == 'l') {
Serial.println ("Level");
lightUpEven ();
}
if (inputString [0] == 'c') {
Serial.println ("Visar tid");
showTime ();
strip.show ();
}
if (inputString [0] == '1') {
Serial.println ("Slå på alla lysdioder");
lightUp (strip. Color (255, 255, 255));
strip.show ();
}
if (inputString [0] == '0') {
Serial.println ("Rensningsremsa");
klar();
strip.show ();
}
// #3, 255 skulle sätta led nummer 3 till nivå 255, 255, 255
if (inputString [0] == '#') {
Strängtemp;
temp = inputString.substring (1);
int pixNum = temp.toInt ();
temp = inputString.substring (inputString.indexOf (',') + 1);
int intensitet = temp.toInt ();
Serial.print ("Inställning");
Serial.print (pixNum);
Serial.print ("till nivå");
Serial.println (intensitet);
strip.setPixelColor (pixNum, strip. Color (intensitet, intensitet, intensitet));
strip.show ();
}
// #3, 255, 0, 125 skulle sätta led nummer 3 till nivå 255, 0, 125
if (inputString [0] == '$') {
Strängtemp;
temp = inputString.substring (1);
int pixNum = temp.toInt ();
int rIndex = inputString.indexOf (',') + 1;
temp = inputString.substring (rIndex);
int rIntensity = temp.toInt ();
intgIndex = inputString.indexOf (',', rIndex + 1) + 1;
temp = inputString.substring (gIndex);
intgIntensity = temp.toInt ();
int bIndex = inputString.indexOf (',', gIndex + 1) + 1;
temp = inputString.substring (bIndex);
int bIntensity = temp.toInt ();
Serial.print ("Inställning");
Serial.print (pixNum);
Serial.print ("R till");
Serial.print (rIntensity);
Serial.print ("G till");
Serial.print (gIntensity);
Serial.print ("B till");
Serial.println (bIntensity);
strip.setPixelColor (pixNum, strip. Color (rIntensity, gIntensity, bIntensity));
strip.show ();
}
if (inputString [0] == 's') {
Strängtemp;
int timme, minut;
temp = inputString.substring (1);
timme = temp.toInt ();
int rIndex = inputString.indexOf (',') + 1;
temp = inputString.substring (rIndex);
minut = temp.toInt ();
Serial.print ("Visar tid:");
Serial.print (timme);
Serial.print (":");
Serial.print (minut);
showTime (timme, minut);
fördröjning (1000);
}
inputString = "";
stringComplete = false;
}
// fördröjning (1000);
}
voidserialEvent () {
medan (Serial.available ()) {
char inChar = (char) Serial.read ();
inputString += inChar;
if (inChar == '\ n') {
stringComplete = true;
}
fördröjning (1);
}
}
voidclear () {
för (uint16_t i = 0; i <strip.numPixels (); i ++) {
strip.setPixelColor (i, strip. Color (0, 0, 0));
}
}
voidshowTime () {
DateTime nu = rtc.now ();
hourPixel = nu.timme () % 12;
minutPixel = (nu. minut () / 5) % 12 + 12;
klar();
// strip.setPixelColor (hourPixel, strip. Color (40 + 40 * level [hourPixel], 30 + 30 * level [hourPixel], 20 + 20 * level [hourPixel]));
// strip.setPixelColor (minutePixel, strip. Color (40 + 40 * nivå [minutPixel], 30 + 30 * nivå [minutPixel], 20 + 20 * nivå [minutPixel]));
strip.setPixelColor (hourPixel, strip. Color (level [hourPixel], level [hourPixel], level [hourPixel]));
strip.setPixelColor (minutPixel, strip. Color (nivå [minutPixel], nivå [minutPixel], nivå [minutPixel]));
// lightUp (strip. Color (255, 255, 255));
strip.show ();
}
voidshowTime (int timme, int minut) {
hourPixel = timme % 12;
minutPixel = (minut / 5) % 12 + 12;
klar();
// strip.setPixelColor (hourPixel, strip. Color (40 + 40 * level [hourPixel], 30 + 30 * level [hourPixel], 20 + 20 * level [hourPixel]));
// strip.setPixelColor (minutePixel, strip. Color (40 + 40 * nivå [minutPixel], 30 + 30 * nivå [minutPixel], 20 + 20 * nivå [minutPixel]));
strip.setPixelColor (hourPixel, strip. Color (level [hourPixel], level [hourPixel], level [hourPixel]));
strip.setPixelColor (minutPixel, strip. Color (nivå [minutPixel], nivå [minutPixel], nivå [minutPixel]));
// lightUp (strip. Color (255, 255, 255));
strip.show ();
}
voidlightUp (uint32_t färg) {
för (uint16_t i = 0; i <strip.numPixels (); i ++) {
strip.setPixelColor (i, färg);
}
strip.show ();
}
voidlightUpEven () {
för (uint16_t i = 0; i <strip.numPixels (); i ++) {
strip.setPixelColor (i, strip. Color (nivå , nivå , nivå ));
}
strip.show ();
}

visa rawplywoodClock.ino värd med ❤ av GitHub

Steg 10: Datorsyn - Kalibrering

Datorsyn - Kalibrering
Datorsyn - Kalibrering
Datorsyn - Kalibrering
Datorsyn - Kalibrering

Jag gjorde ett medvetet val att inte använda faner i detta projekt. Om jag hade det hade trätjockleken varit densamma framför alla lysdioder. Men eftersom jag har en annan tjocklek av trä framför varje LED och eftersom träfärgen också varierar mycket är LED -ljusstyrkan olika för varje LED. För att få alla lysdioder att verka av samma ljusstyrka, tänkte jag ut ett snyggt trick.

Jag skrev en bearbetningskod (på GitHub) som tar ett foto av klockan och analyserar ljusstyrkan på varje LED i tur och ordning. Det varierar sedan effekten till varje lysdiod för att försöka få dem alla att ha samma ljusstyrka som den svagaste lysdioden. Nu vet jag att detta är överkill, men bildbehandling är mycket roligt! Och jag hoppas kunna utveckla kalibreringskoden som ett bibliotek.

Du kan se LED -ljusstyrkan före och efter kalibrering på bilderna ovan.

kalibreraDispllay.pde

importprocessing.video.*;
importprocessing.serial.*;
Seriell myPort;
Fånga video;
finalint numLed = 24;
int ledNum = 0;
// du måste ha dessa globala variabler för att kunna använda PxPGetPixelDark ()
int rDark, gDark, bDark, aDark;
int rLed, gLed, bLed, aLed;
int rOrg, gOrg, bOrg, aOrg;
int rTemp, gTemp, bTemp, aTemp;
PImage ourImage;
int runNumber = 0;
int acceptabelt fel = 3;
int gjort;
int numPixelsInLed;
lång ledIntensitet;
int ledPower;
lång targetIntensity = 99999999;
voidsetup () {
gjort = newint [numLed];
numPixelsInLed = newint [numLed];
ledIntensity = newlong [numLed];
ledPower = newint [numLed];
för (int i = 0; i <numLed; i ++) {
ledPower = 255;
}
printArray (Serial.list ());
String portName = Serial.list () [31];
myPort = newSerial (detta, portnamn, 9600);
storlek (640, 480);
video = newCapture (detta, bredd, höjd);
video.start ();
noStroke ();
slät();
fördröjning (1000); // Vänta tills seriell port öppnas
}
voiddraw () {
if (video.available ()) {
if (gjort [ledNum] == 0) {
clearDisplay ();
fördröjning (1000);
video.read ();
bild (video, 0, 0, bredd, höjd); // Rita webbkameravideon på skärmen
saveFrame ("data/no_leds.jpg");
if (runNumber! = 0) {
if ((ledIntensity [ledNum] - targetIntensity)*100/targetIntensity> acceptableError) {
ledPower [ledNum] -= pow (0,75, runNumber)*100+1;
}
if ((targetIntensity - ledIntensity [ledNum])*100/targetIntensity> acceptableError) {
ledPower [ledNum] += pow (0,75, runNumber)*100 +1;
}
if (abs (targetIntensity - ledIntensity [ledNum])*100/targetIntensity <= acceptableError) {
gjort [ledNum] = 1;
print ("Led");
print (ledNum);
print ("gjort");
}
if (ledPower [ledNum]> 255) {
ledPower [ledNum] = 255;
}
if (ledPower [ledNum] <0) {
ledPower [ledNum] = 0;
}
}
setLedPower (ledNum, ledPower [ledNum]);
fördröjning (1000);
video.read ();
bild (video, 0, 0, bredd, höjd); // Rita webbkameravideon på skärmen
fördröjning (10);
medan (myPort.available ()> 0) {
int inByte = myPort.read ();
// print (char (inByte));
}
String imageName = "data/";
imageName+= str (ledNum);
imageName += "_ led.jpg";
saveFrame (imageName);
String originalImageName = "data/org";
originalImageName+= str (ledNum);
originalImageName += ". jpg";
if (runNumber == 0) {
saveFrame (originalImageName);
}
PImage noLedImg = loadImage ("data/no_leds.jpg");
PImage ledImg = loadImage (imageName);
PImage originalImg = loadImage (originalImageName);
noLedImg.loadPixels ();
ledImg.loadPixels ();
originalImg.loadPixels ();
bakgrund (0);
loadPixels ();
ledIntensity [ledNum] = 0;
numPixelsInLed [ledNum] = 0;
för (int x = 0; x <bredd; x ++) {
för (int y = 0; y <höjd; y ++) {
PxPGetPixelDark (x, y, noLedImg.pixels, bredd);
PxPGetPixelLed (x, y, ledImg.pixels, bredd);
PxPGetPixelOrg (x, y, originalImg.pixels, bredd);
if ((rOrg+gOrg/2+bOrg/3)-(rDark+gDark/2+bDark/3)> 75) {
ledIntensity [ledNum] = ledIntensity [ledNum]+(rLed+gLed/2+bLed/3) -(rDark+gDark/2+bDark/3);
rTemp = 255;
gTemp = 255;
bTemp = 255;
numPixelsInLed [ledNum] ++;
} annat {
rTemp = 0;
gTemp = 0;
bTemp = 0;
}
PxPSetPixel (x, y, rTemp, gTemp, bTemp, 255, pixlar, bredd);
}
}
ledIntensity [ledNum] /= numPixelsInLed [ledNum];
if (targetIntensity> ledIntensity [ledNum] && runNumber == 0) {
targetIntensity = ledIntensity [ledNum];
}
updatePixels ();
}
print (ledNum);
print (',');
print (ledPower [ledNum]);
print (',');
println (ledIntensity [ledNum]);
ledNum ++;
if (ledNum == numLed) {
int donezo = 0;
för (int i = 0; i <numLed; i ++) {
donezo += gjort ;
}
if (donezo == numLed) {
println ("KLAR");
för (int i = 0; i <numLed; i ++) {
tryck (i);
print ("\ t");
println (ledPower );
}
print ("int nivå [");
print (ledNum);
print ("] = {");
för (int i = 0; i <numLed-1; i ++) {
print (ledPower );
print (',');
}
print (ledPower [numLed -1]);
println ("};");
lightUpEven ();
medan (sant);
}
print ("Målintensitet:");
if (runNumber == 0) {
targetIntensity -= 1;
}
println (targetIntensity);
ledNum = 0;
runNumber ++;
}
}
}
voidPxPGetPixelOrg (intx, inty, int pixelArray, intpixelsWidth) {
int thisPixel = pixelArray [x+y*pixelsWidth]; // få färgerna som en int från pixlarna
aOrg = (thisPixel >> 24) & 0xFF; // vi måste skifta och maskera för att få varje komponent ensam
rOrg = (thisPixel >> 16) & 0xFF; // detta är snabbare än att ringa röd (), grön (), blå ()
gOrg = (thisPixel >> 8) & 0xFF;
bOrg = thisPixel & 0xFF;
}
voidPxPGetPixelDark (intx, inty, int pixelArray, intpixelsWidth) {
int thisPixel = pixelArray [x+y*pixelsWidth]; // få färgerna som en int från pixlarna
aDark = (thisPixel >> 24) & 0xFF; // vi måste skifta och maskera för att få varje komponent ensam
rDark = (thisPixel >> 16) & 0xFF; // detta är snabbare än att ringa röd (), grön (), blå ()
gDark = (thisPixel >> 8) & 0xFF;
bDark = thisPixel & 0xFF;
}
voidPxPGetPixelLed (intx, inty, int pixelArray, intpixelsWidth) {
int thisPixel = pixelArray [x+y*pixelsWidth]; // få färgerna som en int från pixlarna
aLed = (thisPixel >> 24) & 0xFF; // vi måste skifta och maskera för att få varje komponent ensam
rLed = (thisPixel >> 16) & 0xFF; // detta är snabbare än att ringa röd (), grön (), blå ()
gLed = (thisPixel >> 8) & 0xFF;
bLed = thisPixel & 0xFF;
}
voidPxPSetPixel (intx, inty, intr, intg, intb, inta, int pixelArray, intpixelsWidth) {
a = (a << 24);
r = r << 16; // Vi packar alla fyra komposenterna i ett int
g = g << 8; // så vi måste flytta dem till deras platser
färg argb = a | r | g | b; // binär "eller" operation lägger till dem alla till en int
pixelArray [x+y*pixelsWidth] = argb; // slutligen satte vi int med te färger i pixlarna
}

visa rawcalibrateDispllay.pde värd med ❤ av GitHub

Steg 11: Avskedande anmärkningar

Fallgropar att undvika:

* Med trä får du vad du betalar för. Så skaffa trä av god kvalitet. Björkplywood är ett bra val; alla ljusa massivt trä kommer att göra bra också. Jag billigade ut på skogen och ångrar mitt beslut.

* Det är bättre att borra mindre än mer. Ett par hål gick för djupt för min bit. Och epoxin visar sig på framsidan. Det är väldigt märkbart när du märker det.

* Använd en borrkrona istället för en rak ände. Jag har inte experimenterat med bolländbiten, men jag är ganska säker på att resultaten kommer att bli mycket bättre.

Jag flörtar med tanken på att sälja dessa på Etsy eller tindie. Jag skulle verkligen uppskatta om du kunde kommentera nedan om du tycker att det är vettigt:)

Rekommenderad: