Índice:

Relógio LED de madeira - Estilo analógico: 11 etapas (com imagens)
Relógio LED de madeira - Estilo analógico: 11 etapas (com imagens)

Vídeo: Relógio LED de madeira - Estilo analógico: 11 etapas (com imagens)

Vídeo: Relógio LED de madeira - Estilo analógico: 11 etapas (com imagens)
Vídeo: Expectativa x realidade. E você lindona, Opção 1 ou 2? 2024, Novembro
Anonim
Relógio LED de madeira - estilo analógico
Relógio LED de madeira - estilo analógico

É um relógio LED de madeira de estilo analógico. Não sei por que não vi um desses antes … embora os tipos digitais sejam muito comuns. Anyhoo, vamos lá!

Passo 1:

Imagem
Imagem
Imagem
Imagem

O projeto do relógio de madeira compensada começou como um projeto inicial simples para a fresadora CNC. Eu estava procurando projetos simples online e encontrei esta lâmpada (imagem acima). Também tinha visto relógios digitais que brilham através do folheado de madeira (imagem acima). Portanto, combinar os dois projetos foi uma ideia óbvia. Procurando me desafiar, decidi não usar folheado, mas apenas um pedaço de madeira para este projeto.

Etapa 2: Design

Projeto
Projeto
Projeto
Projeto

Eu projetei o relógio no Inkscape (imagem acima). O design é muito simples por escolha. Decidi não rotear os rastreamentos para os fios porque, a essa altura, eu não tinha certeza se queria usar fiação radial ou de perímetro. (Decidi finalmente ir com a fiação de perímetro.) Um neopixel vai em cada um dos pequenos orifícios circulares para mostrar o tempo de minuto e hora, com precisão de cinco minutos. O círculo no meio será direcionado para acomodar os componentes eletrônicos.

Etapa 3: CNC

CNCing
CNCing
CNCing
CNCing
CNCing
CNCing
CNCing
CNCing

Eu projetei os caminhos da ferramenta no MasterCAM e usei um technoRouter para fresar o relógio em compensado de 3/4 de polegada. Eu uso uma peça de 15 "x 15" para isso, com desperdício mínimo. O truque é rotear o máximo possível da madeira sem quebrar a madeira. Deixar 0,05 "-0,1" é uma boa escolha para madeira leve. Se você não tiver certeza, é melhor deixar mais madeira, porque você sempre pode lixar a outra face. Acabei removendo um pouco demais de madeira de algumas peças, mas felizmente os resultados não sofreram muito com isso.

Nota para usuários sem acesso a um CNC:

Este projeto pode ser feito facilmente com uma furadeira. Você só precisa definir o limite em um ponto em que deixa cerca de 0,1 de madeira restante na base. Você terá que ser preciso, mas não muito preciso. Afinal, o ideal é que ninguém veja todos os LEDs acendendo em ao mesmo tempo, para que você possa se safar com um pouco de sujeira.

Etapa 4: Eletrônica

Eletrônicos
Eletrônicos
Eletrônicos
Eletrônicos
Eletrônicos
Eletrônicos

A eletrônica é bastante simples. São 24 neopixels, doze para mostrar as horas e doze para mostrar os minutos, com precisão de cinco minutos. Um mini Arduino pro controla os neopixels e obtém o tempo preciso por meio de um módulo de relógio em tempo real (RTC) DS3231. O módulo RTC possui uma célula tipo moeda como reserva, de forma que não perde tempo mesmo com a alimentação desligada.

Material:

Arduino pro mini (ou qualquer outro Arduino)

Placa de breakout DS3231

Neopixels em placas de breakout individuais

Etapa 5: Montagem de eletrônicos

Montagem Eletrônica
Montagem Eletrônica
Montagem Eletrônica
Montagem Eletrônica
Montagem Eletrônica
Montagem Eletrônica
Montagem Eletrônica
Montagem Eletrônica

Eu conectei os neopixels em uma corda, usando fios de 2,5 para os primeiros doze leds e fio de quatro polegadas para os doze seguintes. Eu poderia ter usado comprimentos de fio um pouco menores. Depois de fazer a corda, testei, verificando a solda juntas estavam boas. Eu adicionei um interruptor momentâneo para ligar todos os leds, apenas para mostrar.

Etapa 6: Operação a seco

Funcionamento a seco
Funcionamento a seco
Funcionamento a seco
Funcionamento a seco
Funcionamento a seco
Funcionamento a seco
Funcionamento a seco
Funcionamento a seco

Depois de experimentar, colocar LEDs nos orifícios e ligá-los todos, fiquei satisfeito com os resultados. Então, lixei um pouco a face frontal e apliquei uma camada de PU. Acabei lixando o casaco mais tarde, mas é uma boa ideia deixá-lo colocado se você não achar desagradável esteticamente.

Etapa 7: epóxi

Epóxi
Epóxi
Epóxi
Epóxi

Após alguns testes com a posição do led dentro dos orifícios, descobri que a melhor discussão é alcançada quando os LEDs estão a cerca de 0,2 de distância do final do orifício. Quando você tentar fazer isso, o brilho dos LEDs será muito diferente em cada furo. Não se preocupe com isso; vamos corrigi-lo no código. Isso se deve ao tipo de broca que usei. Se eu fizesse isso novamente, usaria uma broca de ponta esférica para os furos. Mas, em todo caso, para pegar a distância misturei um pouco de epóxi e coloquei um pouquinho em cada furo.

Etapa 8: juntando tudo

Juntando tudo
Juntando tudo
Juntando tudo
Juntando tudo
Juntando tudo
Juntando tudo
Juntando tudo
Juntando tudo

Os LEDs serão colocados a partir da posição do ponteiro das 12 horas, movendo-se no sentido anti-horário, passando por todas as posições dos ponteiros das horas e, em seguida, para o ponteiro dos minutos, novamente movendo-se a partir da marca de 60 minutos movendo-se no sentido anti-horário. Isso ocorre para que, quando observamos de frente, o padrão de LED apareça no sentido horário.

Depois que o epóxi curou por uma hora, coloquei um pouco mais de epóxi. Desta vez, coloquei os LEDs nos orifícios, certificando-se de cobrir os fios e as juntas de solda com o epóxi. Isso contribui para uma boa difusão de luz e protege os fios.

Etapa 9: Código

Código
Código

O código está no GitHub, fique à vontade para modificá-lo para seu uso. Quando você liga todos os LEDs no mesmo nível, o brilho da luz que brilha será muito diferente em cada orifício. Isso se deve às diferentes espessuras de madeira nos furos e à diferença de tonalidade da madeira, como você pode ver a cor da madeira varia bastante na minha peça. Para remediar essa diferença de brilho, fiz uma matriz de níveis de brilho do led. E diminuiu o brilho dos LEDs mais brilhantes. É um processo de tentativa e erro e pode levar vários minutos, mas os resultados valem a pena.

plywoodClock.ino

// Plywood Clock
// Autor: tinkrmind
// Atribuição 4.0 Internacional (CC BY 4.0). Você é livre para:
// Compartilhar - copia e redistribui o material em qualquer meio ou formato
// Adaptar - remixar, transformar e construir sobre o material para qualquer propósito, mesmo comercialmente.
// Viva!
#incluir
#include "RTClib.h"
RTC_DS3231 rtc;
#include "Adafruit_NeoPixel.h"
#ifdef _AVR_
#incluir
#fim se
# definePIN6
Tira Adafruit_NeoPixel = Adafruit_NeoPixel (60, PIN, NEO_GRB + NEO_KHZ800);
int hourPixel = 0;
int minutePixel = 0;
unsignedlong lastRtcCheck;
String inputString = ""; // uma string para conter os dados de entrada
boolean stringComplete = false; // se a string está completa
nível interno [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
while (! Serial); // para Leonardo / Micro / Zero
#fim se
// Isso é para Trinket 5V 16MHz, você pode remover essas três linhas se não estiver usando um Trinket
#if definido (_AVR_ATtiny85_)
if (F_CPU == 16000000) clock_prescale_set (clock_div_1);
#fim se
// Fim do código especial do trinket
Serial.begin (9600);
strip.begin ();
strip.show (); // Inicializa todos os pixels para 'desligado'
if (! rtc.begin ()) {
Serial.println ("Não foi possível encontrar RTC");
enquanto (1);
}
pinMode (2, INPUT_PULLUP);
// rtc.adjust (DateTime (F (_ DATE_), F (_ TIME_)));
if (rtc.lostPower ()) {
Serial.println ("RTC perdeu energia, vamos definir a hora!");
// a linha seguinte define o RTC para a data e hora em que este esboço foi compilado
rtc.adjust (DateTime (F (_ DATE_), F (_ TIME_)));
// Esta linha define o RTC com uma data e hora explícitas, por exemplo, para definir
// 21 de janeiro de 2014 às 3h, você ligaria:
// rtc.adjust (DateTime (2017, 11, 06, 2, 49, 0));
}
// rtc.adjust (DateTime (2017, 11, 06, 2, 49, 0));
// lightUpEven ();
// enquanto (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 ();
altura de começar();
lastRtcCheck = millis ();
}
if (! digitalRead (2)) {
lightUpEven ();
}
if (stringComplete) {
Serial.println (inputString);
if (inputString [0] == 'l') {
Serial.println ("Nível");
lightUpEven ();
}
if (inputString [0] == 'c') {
Serial.println ("Mostrar tempo");
altura de começar();
strip.show ();
}
if (inputString [0] == '1') {
Serial.println ("Ligar todos os LEDs");
lightUp (strip. Color (255, 255, 255));
strip.show ();
}
if (inputString [0] == '0') {
Serial.println ("Faixa de limpeza");
Claro();
strip.show ();
}
// # 3, 255 definiria o led número 3 para o nível 255, 255, 255
if (inputString [0] == '#') {
String temp;
temp = inputString.substring (1);
int pixNum = temp.toInt ();
temp = inputString.substring (inputString.indexOf (',') + 1);
intensidade int = temp.toInt ();
Serial.print ("Configuração");
Serial.print (pixNum);
Serial.print ("para o nível");
Serial.println (intensidade);
strip.setPixelColor (pixNum, strip. Color (intensidade, intensidade, intensidade));
strip.show ();
}
// # 3, 255, 0, 125 definiria o led número 3 para o nível 255, 0, 125
if (inputString [0] == '$') {
String temp;
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 b Intensidade = temp.toInt ();
Serial.print ("Configuração");
Serial.print (pixNum);
Serial.print ("R para");
Serial.print (rIntensity);
Serial.print ("G para");
Serial.print (gIntensity);
Serial.print ("B para");
Serial.println (bIntensity);
strip.setPixelColor (pixNum, strip. Color (rIntensity, gIntensity, bIntensity));
strip.show ();
}
if (inputString [0] == 's') {
String temp;
hora, minuto int;
temp = inputString.substring (1);
hora = temp.toInt ();
int rIndex = inputString.indexOf (',') + 1;
temp = inputString.substring (rIndex);
minuto = temp.toInt ();
Serial.print ("Mostrar tempo:");
Serial.print (hora);
Serial.print (":");
Serial.print (minuto);
showTime (hora, minuto);
atraso (1000);
}
inputString = "";
stringComplete = false;
}
// delay (1000);
}
voidserialEvent () {
while (Serial.available ()) {
char inChar = (char) Serial.read ();
inputString + = inChar;
if (inChar == '\ n') {
stringComplete = true;
}
atraso (1);
}
}
voidclear () {
para (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;
Claro();
// strip.setPixelColor (hourPixel, strip. Color (40 + 40 * nível [hourPixel], 30 + 30 * nível [hourPixel], 20 + 20 * nível [hourPixel]));
// strip.setPixelColor (minutePixel, strip. Color (40 + 40 * nível [minutePixel], 30 + 30 * nível [minutePixel], 20 + 20 * nível [minutePixel]));
strip.setPixelColor (hourPixel, strip. Color (nível [hourPixel], nível [hourPixel], nível [hourPixel]));
strip.setPixelColor (minutePixel, strip. Color (nível [minutePixel], nível [minutePixel], nível [minutePixel]));
// lightUp (strip. Color (255, 255, 255));
strip.show ();
}
voidshowTime (int hora, int minuto) {
hourPixel = hora% 12;
minutePixel = (minuto / 5)% 12 + 12;
Claro();
// strip.setPixelColor (hourPixel, strip. Color (40 + 40 * nível [hourPixel], 30 + 30 * nível [hourPixel], 20 + 20 * nível [hourPixel]));
// strip.setPixelColor (minutePixel, strip. Color (40 + 40 * nível [minutePixel], 30 + 30 * nível [minutePixel], 20 + 20 * nível [minutePixel]));
strip.setPixelColor (hourPixel, strip. Color (nível [hourPixel], nível [hourPixel], nível [hourPixel]));
strip.setPixelColor (minutePixel, strip. Color (nível [minutePixel], nível [minutePixel], nível [minutePixel]));
// lightUp (strip. Color (255, 255, 255));
strip.show ();
}
voidlightUp (uint32_t color) {
para (uint16_t i = 0; i <strip.numPixels (); i ++) {
strip.setPixelColor (i, color);
}
strip.show ();
}
voidlightUpEven () {
para (uint16_t i = 0; i <strip.numPixels (); i ++) {
strip.setPixelColor (i, strip. Color (nível , nível , nível ));
}
strip.show ();
}

veja rawplywoodClock.ino hospedado com ❤ por GitHub

Etapa 10: Visão por Computador - Calibração

Visão Computacional - Calibração
Visão Computacional - Calibração
Visão Computacional - Calibração
Visão Computacional - Calibração

Fiz uma escolha consciente de não usar verniz neste projeto. Se tivesse, a espessura da madeira seria a mesma na frente de todos os LEDs. Mas, como eu tenho uma espessura de madeira diferente na frente de cada LED e porque a cor da madeira também varia muito, o brilho do LED é diferente para cada LED. Para fazer todos os LEDs parecerem ter o mesmo brilho, inventei um truque bacana.

Eu escrevi um código de processamento (no GitHub) que tira uma foto do relógio e analisa o brilho de cada LED por vez. Em seguida, varia a potência de cada LED para tentar fazer com que todos tenham o mesmo brilho que o LED mais escuro. Bem, eu sei que isso é um exagero, mas o processamento de imagens é muito divertido! E espero desenvolver o código de calibração como uma biblioteca.

Você pode ver o brilho do LED antes e depois da calibração nas fotos acima.

calibrateDispllay.pde

importprocessing.video. *;
importprocessing.serial. *;
Serial myPort;
Capturar vídeo;
finalint numLed = 24;
int ledNum = 0;
// você deve ter essas variáveis globais para usar o 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 aceitávelError = 3;
int feito;
int numPixelsInLed;
long ledIntensity;
int ledPower;
long targetIntensity = 99999999;
voidsetup () {
feito = newint [numLed];
numPixelsInLed = newint [numLed];
ledIntensity = newlong [numLed];
ledPower = newint [numLed];
para (int i = 0; i <numLed; i ++) {
ledPower = 255;
}
printArray (Serial.list ());
String portName = Serial.list () [31];
myPort = newSerial (this, portName, 9600);
tamanho (640, 480);
video = newCapture (this, width, height);
video.start ();
noStroke ();
suave();
atraso (1000); // Aguarde a porta serial abrir
}
voiddraw () {
if (video.available ()) {
if (feito [ledNum] == 0) {
clearDisplay ();
atraso (1000);
video.read ();
imagem (vídeo, 0, 0, largura, altura); // Desenhe o vídeo da webcam na tela
saveFrame ("data / no_leds.jpg");
if (runNumber! = 0) {
if ((ledIntensity [ledNum] - targetIntensity) * 100 / targetIntensity> aceitávelError) {
ledPower [ledNum] - = pow (0,75, runNumber) * 100 + 1;
}
if ((targetIntensity - ledIntensity [ledNum]) * 100 / targetIntensity> aceitávelError) {
ledPower [ledNum] + = pow (0,75, runNumber) * 100 + 1;
}
if (abs (targetIntensity - ledIntensity [ledNum]) * 100 / targetIntensity <= aceitávelError) {
concluído [ledNum] = 1;
imprimir ("Led");
imprimir (ledNum);
imprimir ("pronto");
}
if (ledPower [ledNum]> 255) {
ledPower [ledNum] = 255;
}
if (ledPower [ledNum] <0) {
ledPower [ledNum] = 0;
}
}
setLedPower (ledNum, ledPower [ledNum]);
atraso (1000);
video.read ();
imagem (vídeo, 0, 0, largura, altura); // Desenhe o vídeo da webcam na tela
atraso (10);
while (myPort.available ()> 0) {
int inByte = myPort.read ();
// print (char (inByte));
}
String imageName = "dados /";
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 ();
fundo (0);
loadPixels ();
ledIntensity [ledNum] = 0;
numPixelsInLed [ledNum] = 0;
para (int x = 0; x <largura; x ++) {
para (int y = 0; y <altura; y ++) {
PxPGetPixelDark (x, y, noLedImg.pixels, largura);
PxPGetPixelLed (x, y, ledImg.pixels, largura);
PxPGetPixelOrg (x, y, originalImg.pixels, largura);
if ((rOrg + gOrg / 2 + bOrg / 3) - (rOrg + 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] ++;
} outro {
rTemp = 0;
gTemp = 0;
bTemp = 0;
}
PxPSetPixel (x, y, rTemp, gTemp, bTemp, 255, pixels, largura);
}
}
ledIntensity [ledNum] / = numPixelsInLed [ledNum];
if (targetIntensity> ledIntensity [ledNum] && runNumber == 0) {
targetIntensity = ledIntensity [ledNum];
}
updatePixels ();
}
imprimir (ledNum);
imprimir(', ');
imprimir (ledPower [ledNum]);
imprimir(', ');
println (ledIntensity [ledNum]);
ledNum ++;
if (ledNum == numLed) {
int donezo = 0;
para (int i = 0; i <numLed; i ++) {
donezo + = feito ;
}
if (donezo == numLed) {
println ("FEITO");
para (int i = 0; i <numLed; i ++) {
imprimir (i);
imprimir ("\ t");
println (ledPower );
}
imprimir ("nível interno [");
imprimir (ledNum);
imprimir ("] = {");
para (int i = 0; i <numLed-1; i ++) {
imprimir (ledPower );
imprimir(', ');
}
imprimir (ledPower [numLed -1]);
println ("};");
lightUpEven ();
enquanto (verdadeiro);
}
imprimir ("Intensidade alvo:");
if (runNumber == 0) {
targetIntensity - = 1;
}
println (targetIntensity);
ledNum = 0;
runNumber ++;
}
}
}
voidPxPGetPixelOrg (intx, inty, int pixelArray, intpixelsWidth) {
int thisPixel = pixelArray [x + y * pixelsWidth]; // obtendo as cores como um inteiro dos pixels
aOrg = (thisPixel >> 24) & 0xFF; // precisamos mudar e mascarar para obter cada componente sozinho
rOrg = (thisPixel >> 16) & 0xFF; // isso é mais rápido do que chamar red (), green (), blue ()
gOrg = (thisPixel >> 8) & 0xFF;
bOrg = thisPixel & 0xFF;
}
voidPxPGetPixelDark (intx, inty, int pixelArray, intpixelsWidth) {
int thisPixel = pixelArray [x + y * pixelsWidth]; // obtendo as cores como um inteiro dos pixels
aDark = (thisPixel >> 24) & 0xFF; // precisamos mudar e mascarar para obter cada componente sozinho
rDark = (thisPixel >> 16) & 0xFF; // isso é mais rápido do que chamar red (), green (), blue ()
gDark = (thisPixel >> 8) & 0xFF;
bDark = thisPixel & 0xFF;
}
voidPxPGetPixelLed (intx, inty, int pixelArray, intpixelsWidth) {
int thisPixel = pixelArray [x + y * pixelsWidth]; // obtendo as cores como um inteiro dos pixels
aLed = (thisPixel >> 24) & 0xFF; // precisamos mudar e mascarar para obter cada componente sozinho
rLed = (thisPixel >> 16) & 0xFF; // isso é mais rápido do que chamar red (), green (), blue ()
gLed = (thisPixel >> 8) & 0xFF;
bLed = thisPixel & 0xFF;
}
voidPxPSetPixel (intx, inty, intr, intg, intb, inta, int pixelArray, intpixelsWidth) {
a = (a << 24);
r = r << 16; // Estamos empacotando todos os 4 compostos em um único
g = g << 8; // então precisamos mudá-los para seus lugares
cor argb = a | r | g | b; // operação binária "ou" adiciona todos eles em um único int
pixelArray [x + y * pixelsWidth] = argb; // finalmente definimos o int com as cores nos pixels
}

veja rawcalibrateDispllay.pde hospedado com ❤ por GitHub

Etapa 11: observações de despedida

Armadilhas a evitar:

* Com madeira, você obtém o que pagou. Portanto, obtenha madeira de boa qualidade. Contraplacado de bétula é uma boa escolha; qualquer madeira leve e sólida também serve. Eu roubei da madeira e lamento minha decisão.

* É melhor perfurar menos do que mais. Alguns buracos foram muito profundos para a minha peça. E o epóxi fica visível na face frontal. É muito perceptível quando você percebe.

* Use uma broca de ponta esférica em vez de uma ponta reta. Não experimentei a ponta da bola, mas tenho quase certeza de que os resultados serão muito melhores.

Estou flertando com a ideia de vendê-los no Etsy ou no Tindie. Eu realmente apreciaria se você pudesse comentar abaixo se você acha que faz sentido:)

Recomendado: