Sommario:

Orologio LED in Legno - Stile Analogico: 11 Passi (con Immagini)
Orologio LED in Legno - Stile Analogico: 11 Passi (con Immagini)

Video: Orologio LED in Legno - Stile Analogico: 11 Passi (con Immagini)

Video: Orologio LED in Legno - Stile Analogico: 11 Passi (con Immagini)
Video: Quando provi a fare un complimento a una ragazza... 2024, Luglio
Anonim
Orologio LED in legno - Stile analogico
Orologio LED in legno - Stile analogico

È un orologio a LED in legno in stile analogico. Non so perché non ne ho mai visto uno prima..anche se i tipi digitali sono molto comuni. Comunque, ci siamo!

Passo 1:

Immagine
Immagine
Immagine
Immagine

Il progetto dell'orologio in compensato è iniziato come un semplice progetto di partenza per il router CNC. Stavo guardando semplici progetti online e ho trovato questa lampada (immagine sopra). Avevo anche visto orologi digitali che brillano attraverso l'impiallacciatura di legno (immagine sopra). Quindi, combinare i due progetti era un'idea ovvia. Cercando di mettermi alla prova, ho deciso di non utilizzare l'impiallacciatura ma solo un pezzo di legno per questo progetto.

Passaggio 2: progettazione

Design
Design
Design
Design

Ho progettato l'orologio in Inkscape (immagine sopra). Il design è molto semplice per scelta. Ho deciso di non instradare le tracce per i fili perché a questo punto non ero sicuro di voler utilizzare un cablaggio radiale o perimetrale. (Ho deciso di utilizzare il cablaggio perimetrale alla fine.) Un neopixel va in ciascuno dei piccoli fori circolari per mostrare l'ora e i minuti, con una precisione di cinque minuti. Il cerchio al centro verrà instradato per ospitare l'elettronica.

Passaggio 3: lavorazione CNC

CNC
CNC
CNC
CNC
CNC
CNC
CNC
CNC

Ho progettato i percorsi utensile su MasterCAM e ho utilizzato un technoRouter per fresare l'orologio da compensato da 3/4 di pollice. Io uso un pezzo 15 "x 15" per questo, con uno spreco minimo. Il trucco è far uscire quanto più legno possibile senza rompere il legno. Lasciare 0,05 "-0,1" è una buona scelta per il legno chiaro. Se non sei sicuro, è meglio lasciare più legno, perché puoi sempre carteggiare l'altra faccia. Ho finito per rimuovere un po' troppo legno da alcune parti, ma per fortuna i risultati non ne risentono troppo.

Nota per gli utenti senza accesso a un CNC:

Questo progetto può essere fatto facilmente con un trapano a colonna. Hai solo bisogno di impostare l'arresto in un punto in cui lasci circa 0,1 di legno rimanente alla base. Dovrai essere preciso, ma non troppo preciso. Dopotutto, idealmente nessuno vedrà accendersi tutti i LED a allo stesso tempo, così puoi farla franca con un po 'di sbavatura.

Passaggio 4: elettronica

Elettronica
Elettronica
Elettronica
Elettronica
Elettronica
Elettronica

L'elettronica è abbastanza semplice. Ci sono 24 neopixel, dodici per mostrare le ore e dodici per mostrare i minuti, con una precisione di cinque minuti. Un Arduino pro mini controlla i neopixel e ottiene l'ora precisa tramite un modulo orologio in tempo reale (RTC) DS3231. Il modulo RTC ha una cella a bottone come backup, quindi non perde tempo anche quando l'alimentazione è spenta.

Materiale:

Arduino pro mini (o qualsiasi altro Arduino per quella materia)

Scheda breakout DS3231

Neopixel in singole schede breakout

Passaggio 5: assemblaggio dell'elettronica

Assemblaggio dell'elettronica
Assemblaggio dell'elettronica
Assemblaggio dell'elettronica
Assemblaggio dell'elettronica
Assemblaggio dell'elettronica
Assemblaggio dell'elettronica
Assemblaggio dell'elettronica
Assemblaggio dell'elettronica

Ho collegato i neopixel in una stringa, usando fili da 2,5 per i primi dodici led e un filo da quattro pollici per i successivi dodici. Avrei potuto usare cavi di lunghezza leggermente inferiore. Dopo aver realizzato la stringa, l'ho testata, assicurandomi che la saldatura le giunture erano buone, ho aggiunto un interruttore momentaneo per accendere tutti i led, tanto per mettersi in mostra.

Passaggio 6: funzionamento a secco

Funzionamento a secco
Funzionamento a secco
Funzionamento a secco
Funzionamento a secco
Funzionamento a secco
Funzionamento a secco
Funzionamento a secco
Funzionamento a secco

Dopo aver sperimentato, mettendo i LED nei fori e accendendoli tutti, sono rimasto soddisfatto dei risultati. Quindi ho carteggiato un po' la faccia anteriore e applicato uno strato di PU. Ho finito per carteggiare il cappotto in seguito, ma è una buona idea lasciarlo acceso se non lo trovi esteticamente sgradevole.

Passaggio 7: resina epossidica

epossidico
epossidico
epossidico
epossidico

Dopo alcuni test con la posizione del led all'interno dei fori, ho pensato che la migliore discussione si ottiene quando i LED si trovano a circa 0,2 di distanza dall'estremità del foro. Quando lo provi tu stesso, la luminosità dei LED sarà molto diversa in ogni foro. Non preoccuparti di questo; lo sistemeremo nel codice. Questo è dovuto al tipo di punta da trapano che ho usato. Se dovessi farlo di nuovo, userei una punta da trapano con estremità sferica per i fori Ma, in ogni caso, per ottenere la distanza ho mescolato un po' di resina epossidica e ne ho messo un po' in ogni foro.

Passaggio 8: mettere tutto insieme

Mettere tutto insieme
Mettere tutto insieme
Mettere tutto insieme
Mettere tutto insieme
Mettere tutto insieme
Mettere tutto insieme
Mettere tutto insieme
Mettere tutto insieme

I LED verranno posizionati a partire dalla posizione della lancetta delle ore 12 spostandosi in senso antiorario attraverso tutte le posizioni della lancetta delle ore e poi fino alla lancetta dei minuti, spostandosi nuovamente dal segno dei 60 minuti in senso antiorario. Questo è così che quando osserviamo di fronte il modello LED appare in senso orario.

Dopo che la resina epossidica si è indurita per un'ora, ho aggiunto dell'altra resina epossidica. Questa volta, ho posizionato i LED nei fori, assicurandomi di coprire i fili e i giunti di saldatura con la resina epossidica. Ciò consente una buona diffusione della luce e protegge i fili.

Passaggio 9: codice

Codice
Codice

Il codice è su GitHub, sentiti libero di modificarlo per il tuo uso. Quando accendi tutti i LED allo stesso livello, la luminosità della luce che attraversa sarà molto diversa in ogni foro. Ciò è dovuto al diverso spessore del legno nei fori e alla differenza nella tonalità del legno, come puoi vedere il colore del legno varia un po' nel mio pezzo. Per rimediare a questa differenza di luminosità, ho realizzato una matrice di livelli di luminosità dei led. E ha diminuito la luminosità dei LED più luminosi. È un processo per tentativi ed errori e può richiedere diversi minuti, ma i risultati ne valgono la pena.

compensatoClock.ino

// Orologio in compensato
// Autore: tinkrmind
// Attribuzione 4.0 Internazionale (CC BY 4.0). Sei libero di:
// Condividi: copia e ridistribuisci il materiale in qualsiasi supporto o formato
// Adatta - remixa, trasforma e costruisci sul materiale per qualsiasi scopo, anche commerciale.
// Evviva!
#includere
#include"RTClib.h"
RTC_DS3231 rtc;
#include"Adafruit_NeoPixel.h"
#ifdef _AVR_
#includere
#finisci se
#definePIN6
Striscia Adafruit_NeoPixel = Adafruit_NeoPixel(60, PIN, NEO_GRB + NEO_KHZ800);
int hourPixel = 0;
int minutePixel = 0;
unsignedlong lastRtcCheck;
stringa inputString = ""; // una stringa per contenere i dati in arrivo
stringa booleanaComplete = false; // se la stringa è completa
livello int[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
mentre (!Seriale); // per Leonardo/Micro/Zero
#finisci se
// Questo è per Trinket 5V 16MHz, puoi rimuovere queste tre linee se non stai usando un Trinket
#se definito (_AVR_ATtiny85_)
if (F_CPU == 16000000) clock_prescale_set(clock_div_1);
#finisci se
// Codice speciale di fine gingillo
Serial.begin(9600);
strip.begin();
strip.show(); // Inizializza tutti i pixel su 'off'
if (! rtc.begin()) {
Serial.println("Impossibile trovare RTC");
mentre (1);
}
pinMode(, INPUT_PULLUP);
// rtc.adjust(DateTime(F(_DATE_), F(_TIME_)));
if (rtc.lostPower()) {
Serial.println("RTC ha perso potenza, impostiamo l'ora!");
// la riga seguente imposta l'RTC sulla data e l'ora in cui questo sketch è stato compilato
rtc.adjust(DateTime(F(_DATE_), F(_TIME_)));
// Questa riga imposta l'RTC con una data e un'ora esplicite, ad esempio per impostare
// 21 gennaio 2014 alle 3 del mattino chiameresti:
// rtc.adjust(DateTime(2017, 11, 06, 2, 49, 0));
}
// rtc.adjust(DateTime(2017, 11, 06, 2, 49, 0));
// lightUpEven();
// mentre (1);
lastRtcCheck = 0;
}
voidloop() {
if (millis() - lastRtcCheck >2000) {
DateTime now = rtc.now();
Serial.print(now.hour(), DEC);
Serial.print(':');
Serial.print(now.minute(), DEC);
Serial.print(':');
Serial.print(now.second(), DEC);
Serial.println();
orario dello spettacolo();
lastRtcCheck = millis();
}
if (!digitalRead(2)) {
lightUpEven();
}
if (stringComplete) {
Serial.println(inputString);
if (inputString[0] == 'l') {
Serial.println("Livello");
lightUpEven();
}
if (inputString[0] == 'c') {
Serial.println("Mostra l'ora");
orario dello spettacolo();
strip.show();
}
if (inputString[0] == '1') {
Serial.println("Accensione di tutti i LED");
lightUp(strip. Color(255, 255, 255));
strip.show();
}
if (inputString[0] == '0') {
Serial.println("Striscia di cancellazione");
chiaro();
strip.show();
}
// #3, 255 imposterebbe il led numero 3 al livello 255, 255, 255
if (inputString[0] == '#') {
Temp. stringa;
temp = inputString.substring(1);
int pixNum = temp.toInt();
temp = inputString.substring(inputString.indexOf(', ') + 1);
int intensità = temp.toInt();
Serial.print("Impostazione");
Serial.print(pixNum);
Serial.print(" al livello ");
Serial.println(intensità);
strip.setPixelColor(pixNum, strip. Color(intensità, intensità, intensità));
strip.show();
}
// #3, 255, 0, 125 imposterebbe il led numero 3 al livello 255, 0, 125
if (inputString[0] == '$') {
Temp. stringa;
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("Impostazione");
Serial.print(pixNum);
Serial.print ("da R a ");
Serial.print(rIntensity);
Serial.print ("da G a ");
Serial.print(gIntensity);
Serial.print ("da B a ");
Serial.println(bIntensity);
strip.setPixelColor(pixNum, strip. Color(rIntensity, gIntensity, bIntensity));
strip.show();
}
if (inputString[0] == 's') {
Temp. stringa;
int ora, minuto;
temp = inputString.substring(1);
ora = temp.toInt();
int rIndex = inputString.indexOf(', ') + 1;
temp = inputString.substring(rIndex);
minuto = temp.toInt();
Serial.print("Ora di visualizzazione: ");
Serial.print(ora);
Serial.print(":");
Serial.print(minuto);
showTime (ora, minuto);
ritardo(1000);
}
inputString = "";
stringaCompleta = falso;
}
// ritardo(1000);
}
voidserialEvent() {
while (Serial.available()) {
char inChar = (char)Serial.read();
inputString += inChar;
if (inChar == '\n') {
stringaCompleta = vero;
}
ritardo(1);
}
}
voidclear() {
for (uint16_t i = 0; i < strip.numPixels(); i++) {
strip.setPixelColor(i, strip. Color(0, 0, 0));
}
}
voidshowTime() {
DateTime now = rtc.now();
hourPixel = now.hour() % 12;
minutePixel = (now.minute() / 5) % 12 + 12;
chiaro();
// strip.setPixelColor(hourPixel, strip. Color(40 + 40 * level[hourPixel], 30 + 30 * level[hourPixel], 20 + 20 * level[hourPixel]));
// strip.setPixelColor(minutePixel, strip. Color(40 + 40 * level[minutePixel], 30 + 30 * level[minutePixel], 20 + 20 * level[minutePixel]));
strip.setPixelColor(hourPixel, strip. Color(level[hourPixel], level[hourPixel], level[hourPixel]));
strip.setPixelColor(minutePixel, strip. Color(level[minutePixel], level[minutePixel], level[minutePixel]));
// lightUp(strip. Color(255, 255, 255));
strip.show();
}
voidshowTime(int ora, int minuto) {
hourPixel = ora % 12;
minutePixel = (minuti / 5) % 12 + 12;
chiaro();
// strip.setPixelColor(hourPixel, strip. Color(40 + 40 * level[hourPixel], 30 + 30 * level[hourPixel], 20 + 20 * level[hourPixel]));
// strip.setPixelColor(minutePixel, strip. Color(40 + 40 * level[minutePixel], 30 + 30 * level[minutePixel], 20 + 20 * level[minutePixel]));
strip.setPixelColor(hourPixel, strip. Color(level[hourPixel], level[hourPixel], level[hourPixel]));
strip.setPixelColor(minutePixel, strip. Color(level[minutePixel], level[minutePixel], level[minutePixel]));
// lightUp(strip. Color(255, 255, 255));
strip.show();
}
voidlightUp(uint32_t color) {
for (uint16_t i = 0; i < strip.numPixels(); i++) {
strip.setPixelColor(i, colore);
}
strip.show();
}
voidlightUpEven() {
for (uint16_t i = 0; i < strip.numPixels(); i++) {
strip.setPixelColor(i, strip. Color(livello, livello, livello));
}
strip.show();
}

visualizza rawplywoodClock.ino ospitato con ❤ da GitHub

Passaggio 10: Visione artificiale - Calibrazione

Visione artificiale - Calibrazione
Visione artificiale - Calibrazione
Visione artificiale - Calibrazione
Visione artificiale - Calibrazione

Ho fatto una scelta consapevole di non usare l'impiallacciatura in questo progetto. Se lo avessi fatto, lo spessore del legno sarebbe stato lo stesso davanti a tutti i LED. Ma, poiché ho uno spessore di legno diverso davanti a ciascun LED e poiché anche il colore del legno varia molto, la luminosità del LED è diversa per ciascun LED. Per far sembrare che tutti i LED abbiano la stessa luminosità, ho escogitato un trucco intelligente.

Ho scritto un codice di elaborazione (su GitHub) che scatta una foto dell'orologio e analizza a turno la luminosità di ciascun LED. Quindi varia la potenza di ciascun LED per cercare di far sì che tutti abbiano la stessa luminosità del LED più fioco. Ora, so che è eccessivo, ma l'elaborazione delle immagini è molto divertente! E spero di sviluppare il codice di calibrazione come una libreria.

Puoi vedere la luminosità del LED prima e dopo la calibrazione nelle foto sopra.

calibrateDisplay.pde

importprocessing.video.*;
importprocessing.serial.*;
myPort seriale;
Cattura video;
finalent numLed =24;
int ledNum =0;
// devi avere queste variabili globali per usare PxPGetPixelDark()
int rScuro, gScuro, bScuro, aScuro;
int rLed, gLed, bLed, aLed;
int rOrg, gOrg, bOrg, aOrg;
int rTemp, gTemp, bTemp, aTemp;
PImage nostraImmagine;
int runNumber =0;
int accettabileError =3;
int fatto;
int numPixelsInLed;
lungo ledIntensità;
int ledPower;
lungo targetIntensity =99999999;
voidsetup() {
done =newint[numLed];
numPixelsInLed =newint[numLed];
ledIntensity =newlong[numLed];
ledPower =newint[numLed];
for (int i=0; i<numLed; i++) {
ledPower =255;
}
printArray(Serial.list());
String nomeporta =Serial.list()[31];
myPort =newSerial(this, portName, 9600);
dimensione (640, 480);
video =newCapture(questo, larghezza, altezza);
video.start();
noStroke();
liscio();
ritardo(1000); // Attendi l'apertura della porta seriale
}
voiddraw() {
if (video.disponibile()) {
if (fatto[num.led] ==0) {
clearDisplay();
ritardo(1000);
video.leggi();
immagine(video, 0, 0, larghezza, altezza); // Disegna il video della webcam sullo schermo
saveFrame("data/no_leds.jpg");
if (runNumber !=0) {
if ((ledIntensity[ledNum] - targetIntensity)*100/targetIntensity > accettabileErrore) {
ledPower[ledNum] -=pow(0.75, runNumber)*100+1;
}
if ((targetIntensity - ledIntensity[ledNum])*100/targetIntensity > accettabileError) {
ledPower[ledNum] +=pow(0.75, runNumber)*100+1;
}
if (abs(targetIntensity - ledIntensity[ledNum])*100/targetIntensity <= errore accettabile) {
done[ledNum] =1;
print("Led");
print(numero led);
print("fatto");
}
if (ledPower[ledNum] >255) {
ledPower[ledNum] =255;
}
if (ledPower[ledNum] <0) {
ledPower[ledNum]=0;
}
}
setLedPower(ledNum, ledPower[ledNum]);
ritardo(1000);
video.leggi();
immagine(video, 0, 0, larghezza, altezza); // Disegna il video della webcam sullo schermo
ritardo(10);
while (myPort.available() >0) {
int inByte = myPort.read();
//print(char(inByte));
}
String imageName ="dati/";
nomeimmagine+=str(ledNum);
nomeimmagine +="_led.jpg";
saveFrame(nomeimmagine);
String originalImageName ="data/org";
originalImageName+=str(ledNum);
nomeImmagineoriginale +=".jpg";
if (runNumber ==0) {
saveFrame(nomeImmagineoriginale);
}
PImage noLedImg =loadImage("data/no_leds.jpg");
PImage ledImg =loadImage(imageName);
PImage originalImg =loadImage(originalImageName);
noLedImg.loadPixels();
ledImg.loadPixels();
originalImg.loadPixels();
sfondo (0);
loadPixel();
ledIntensity[ledNum] =0;
numPixelsInLed[ledNum] =0;
for (int x =0; x<larghezza; x++) {
for (int y =0; y<altezza; y++) {
PxPGetPixelDark(x, y, noLedImg.pixel, larghezza);
PxPGetPixelLed(x, y, ledImg.pixel, larghezza);
PxPGetPixelOrg(x, y, originalImg.pixels, width);
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]++;
} altro {
rTemp=0;
gTemp=0;
bTemp=0;
}
PxPSetPixel(x, y, rTemp, gTemp, bTemp, 255, pixel, larghezza);
}
}
ledIntensity[ledNum] /= numPixelsInLed[ledNum];
if (targetIntensity > ledIntensity[ledNum] && runNumber ==0) {
targetIntensity = ledIntensity[ledNum];
}
updatePixel();
}
print(numero led);
stampa(', ');
print(ledPower[ledNum]);
stampa(', ');
println(ledIntensity[ledNum]);
ledNum++;
if (ledNum == numLed) {
int donezo =0;
for (int i=0; i<numLed; i++) {
donezo += fatto;
}
if (donezo == numLed) {
println("FATTO");
for (int i=0; i<numLed; i++) {
stampa(i);
print("\t");
println(ledPower);
}
print("livello int[");
print(numero led);
print("] = {");
for (int i=0; i<numLed-1; i++) {
print(ledPower);
stampa(', ');
}
print(ledPower[numLed -1]);
println("};");
lightUpEven();
mentre (vero);
}
print("Intensità obiettivo: ");
if (runNumber ==0) {
targetIntensity -=1;
}
println(targetIntensity);
ledNum =0;
runNumber++;
}
}
}
voidPxPGetPixelOrg(intx, inty, int pixelArray, intpixelsWidth) {
int thisPixel=pixelArray[x+y*pixelsWidth]; // ottengo i colori come int dai pixels
aOrg = (thisPixel >>24) &0xFF; // dobbiamo spostare e mascherare per ottenere ogni componente da solo
rOrg = (thisPixel >>16) &0xFF; // questo è più veloce che chiamare red(), green(), blue()
gOrg = (thisPixel >>8) &0xFF;
bOrg = thisPixel &0xFF;
}
voidPxPGetPixelDark(intx, inty, int pixelArray, intpixelsWidth) {
int thisPixel=pixelArray[x+y*pixelsWidth]; // ottengo i colori come int dai pixels
aDark = (thisPixel >>24) &0xFF; // dobbiamo spostare e mascherare per ottenere ogni componente da solo
rDark = (thisPixel >>16) &0xFF; // questo è più veloce che chiamare red(), green(), blue()
gDark = (thisPixel >>8) &0xFF;
bDark = thisPixel &0xFF;
}
voidPxPGetPixelLed(intx, inty, int pixelArray, intpixelsWidth) {
int thisPixel=pixelArray[x+y*pixelsWidth]; // ottengo i colori come int dai pixels
aLed = (thisPixel >>24) &0xFF; // dobbiamo spostare e mascherare per ottenere ogni componente da solo
rLed = (questoPixel >>16) &0xFF; // questo è più veloce che chiamare red(), green(), blue()
gLed = (thisPixel >>8) &0xFF;
bLed = questoPixel &0xFF;
}
voidPxPSetPixel(intx, inty, intr, intg, intb, inta, int pixelArray, intpixelsWidth) {
a =(a<24);
r = r<16; // Stiamo impacchettando tutti e 4 i componenti in un unico int
g = g<8; // quindi dobbiamo spostarli al loro posto
colore argb = a | r | g | B; // operazione binaria "or" li aggiunge tutti in un int
pixelArray[x+y*pixelsWidth]= argb; // finalmente impostiamo l'int con i colori nei pixel
}

visualizza rawcalibrateDispllay.pde ospitato con ❤ da GitHub

Passaggio 11: osservazioni di separazione

Insidie da evitare:

* Con il legno, ottieni quello per cui paghi. Quindi, procurati un legno di buona qualità. Il compensato di betulla è una buona scelta; anche qualsiasi legno massello chiaro andrà bene. Ho perso il legno e mi pento della mia decisione.

* È meglio perforare di meno che di più. Un paio di buchi sono andati troppo in profondità per il mio pezzo. E la resina epossidica si vede attraverso la faccia anteriore. È molto evidente una volta che lo noti.

* Utilizzare una punta da trapano con estremità sferica invece di un'estremità diritta. Non ho sperimentato la punta a sfera, ma sono abbastanza sicuro che i risultati saranno molto migliori.

Sto flirtando con l'idea di venderli su Etsy o tindie. Ti sarei davvero grato se potessi commentare qui sotto se pensi che abbia senso:)

Consigliato: