Manual

do

Maker

.

com

Como fazer um relógio Cuco eletrônico

Como fazer um relógio Cuco eletrônico

Poderia ir direto ao relógio cuco, mas a história da marcação do tempo pela humanidade é incrível! E a evolução dos relógios é fascinante, creio que a maioria deva pensar igual nesse ponto ou, se ainda não, acompanhe a leitura.

Uma breve história do relógio

Relógio do Sol

Do que se sabe na história, o primeiro relógio foi o relógio de sol. A primeira vez que vi um foi no final dos anos 70, em São Vicente. Eu tinha lá meus 6 anos de idade, mas a imagem é viva em minha memória até hoje.

O relógio de sol  foi inventado 3 mil anos antes de Cristo pelos egípcios e babilônios.

relogio_sol-300x260.webp

Relógio de água

Já em 1500 antes de Cristo, uma inscrição funerária egípcia menciona uma clepsidra - um relógio cujo tempo é orientado a água, construído pelo rei Amenophis I. Existem diversas interpretações desse relógio, que marcavam a hora conforme a vazão da água, podendo ser uma boia seu indicador de nível. O formato cônico do recipiente que recebia o líquido era por razão de que o volume adicionava pressão sobre o topo e, conforme o nível descia, a pressão era reduzida, de forma que para marcar o intervalo de 1 hora na base era necessário que a quantidade de líquido fosse suficientemente menor que a hora da medição inicial.

Ampulheta

O grande salto da história do relógio aconteceu em 287 antes de Cristo, com a invenção da roda dentada, por Arquimedes. Inicialmente, não se imagina que faria-se use de engrenagens para um dia medir o tempo de um dia, mas se não fosse pela invenção de Arquimedes, certamente a humanidade teria usado sistemas não portáteis por muito mais tempo. Ainda por muitos anos foi utilizado a clepsidra e o primeiro uso das rodas dentadas para medição de tempo não foi para o relógio, mas para medição dos astros, pelo astrônomo chinês, Y. Hang.

No ano de 250 depois de Cristo, foi inventada então a ampulheta. Seu formato cônico segue o mesmo princípio da clepsidra, porém a medição de tempo sempre foi relativamente curta nesse modelo, que mais se comporta como um cronômetro.

relogio_ampulheta-300x292.webp

 

A vergonha da medição de tempo: A vela

Em 885 depois de Cristo, Alfredo o Grande começou a usar velas para medir o tempo. Não foi o relógio mais criativo, mas funcionava relativamente bem, desde que não influênciado por temperaturas, ventos e mistura diferente da parafina.

Relógio de Galileu

Pulando diversas variações dos modelos citados e alguns outros, chegamos então ao relógio de pêndulo, do Galileu Galilei, que descobriu o isosincronísmo do pêndulo, em 1582. Já cego, em 1640, ditou a seu filho e seu aluno Viviani todos os detalhes do célebre relógio Galileu, o relógio de pêndulo, um dos mais lindos relógios já criados.

galileu_clock-184x300.webp

Relógio Cuco

Em 1730 foi criado o primeiro relógio cuco, na Floresta Negra. E paro por aqui a história do relógio, porque o cuco desse artigo é uma mistura do relógio de Galileu, com o relógio cuco e com o que há de mais moderno na eletrônica digital.

Um relógio cuco Floresta Negra usado chega a custar R$7.000,00! Claro, o relógio é repleto de recursos e autômatas, mas é o mais tradicional dos relógios cuco.

No Mercado Livre se encontra alguns modelos antigos, com valores entre R$1.000,00 e R$5.000,00 (referência: 2018).

relogios_cuco-300x198.webp

Mistura de conceitos

A eletrônica digital nos permite fazer coisas que mecanicamente eram (ou ainda são, em alguns casos) praticamente impossíveis de fazer à mão ou em casa. Não é exatamente o caso de um relógio, cujo mecanismo pode ser tão grande quanto necessário for, mas ainda assim não é simples a ponto de não precisar estudar um bocado. Agora, com muito orgulho, esse deve ser o primeiro relógio que mistura conceitos de épocas tão distantes, iniciando lá no século XVII e vindo até o século XXI. Ainda, um pequeno adicional; invés das 3 rodas, tipico da maioria dos relógios, utilizei o conjunto de 4 rodas, que permite a redução do tamanho das engrenagens.

Antes de começar, vou explicar alguns conceitos (tentarei ser breve).

Funcionamento básico de um relógio

Com exceção do relógio de sol, de água, de areia e o digital (outros?), os demais citados utilizam engrenagens para fazer o movimento. A lógica para qualquer relógio é a mesma;  o conjunto mecânico deve fazer um ciclo por hora. Isso significa que a cada movimento que totalize 60 segundos, a transferência do movimento deve movimentar a "roda" do minuto em 1/60. A engrenagem é chamada de "roda" no mecanismo do relógio, legado do seu surgimento após a roda dentada. Do mesmo modo, ao ciclo de 60 minutos, a transferência deve movimentar a roda de horas em 1/12. Isso significa que o trem de engrenagens deve completar 1 ciclo a cada 3600 segundos.  Partindo disso, deve-se calcular as engrenagens e pinhões para esse propósito.

O conjunto de engrenagens pode variar como bem desejar, mas é necessário considerar que a forma que esse cálculo for feito interferirá diretamente na força aplicada sobre a  engrenagem seguinte; quanto maior o número de eixos, mais difícil será vencer a inércia. Também, quanto menor o pinhão, maior será o esforço. Por exemplo, poderíamos fazer um relógio com apenas 3 engrenagens e sem pinhões, mas olha só isso:

  • 60 segundos = 1 minuto
  • 3600 segundos = 1 hora
  • 42.200 segundos = 24 horas

Suponhamos (apenar para proporcionalizar) que a roda de segundos tenha 60 dentes, 1 para cada segundo (o mais comum é 30 dentes). A roda de horas teria a proporção de 1/720! Não tem como, considerando que os módulos de engrenagens que se tocam deve ser iguais, portanto os dentes devem ter o mesmo tamanho, passo e formato (lembra do artigo sobre como fazer engrenagens?). Por isso que o mecanismo do relógio envolve um conjunto variável de engrenagens, sendo que um dos menores tem 3 rodas e 2 pinhões. Só que para conseguir esse número reduzido de engrenagens, uma exceção foi aberta; a roda de segundos tem 2 minutos, ou seja, completará 1 hora a cada 30 voltas. Isso está relacionado ao tempo do pêndulo, que pode variar tanto em tamanho quanto no prumo, conforme o número de batidas por hora escolhido para criá-lo. Talvez eu faça um artigo do relógio totalmente mecânico, mas fugiria do escopo desse blog, que é o uso de tecnologia eletrônica e digital.

Na atualidade, existem dois artistas muito bons em construção de relógios, sendo um deles francês e o outro, havaiano. Não tirei da minha cabeça os conceitos, eu comprei um projeto há alguns anos e também um livro para calcular mecanismos de relógio e aprender os conceitos envolvidos. Esse sistema simplificado de engrenagens é baseado no projeto havaiano, que foi inspirado no relógio de Galileu. No momento estou finalizando um segundo relógio, bem mais complexo no sistema de engrenagens, mas com um custo final muito menor. E outros virão, onde incluirei fases da lua de forma mecânica, dias do mês e/ou da semana etc.

No artigo sobre engrenagens expliquei um pouco sobre redução. Quando uma engrenagem menor interage com uma maior, o esforço mecânico na roda menor reduz. Já quando a roda maior gira a menor, ela transforma a energia em velocidade e o custo disso é a força. Meu primeiro relógio de madeira foi cortado à mão, com as engrenagens tendo o dobro do tamanho, mas funcionou por pouco tempo porque não respeitei a bitola do eixo e as engrenagens penderam com o peso da corda (5kg). O relógio eu mantive mesmo parado (porque eu me apaixonei por ele) e até hoje não troquei os eixos porque implicará em furar as engrenagens novamente. Qualquer diferença na simetria será danoso. O relógio é esse:

relogio_pendulo-dobitaobyte-225x300.webp

Ele é totalmente mecânico. Existe um cálculo complexo também para o pêndulo, incluindo cálculo de inércia. Fora isso, não dá pra considerar a quantidade de atrito porque varia de madeira para madeira, temperatura e umidade do ar, por isso o peso na borda do pêndulo deve ser ajustado para conseguir o intervalo de tempo correto na troca de dente.

Esse relógio de pêndulo já foi artigo também.

No relógio, o conjunto de engrenagens fundamental é chamado de "trem de rodas".

Como vou usar o mesmo trem de rodas do modelo cortado à mão, apenas reduzindo a escala, a roda de segundos continuará sendo dentada, mas não vou adicionar pêndulo. Na verdade, seria muito feio só o pinhão rodando ou, uma roda "banguela" por não ter funcionalidade. Mas a roda de segundos não é dispensável. Como acabei de citar, atrás dela tem um pinhão de 7 dentes. Ele atuará sobre uma roda de 60 dentes, que também possui um pinhão de 7 dentes. Esse segundo pinhão atua na roda de minutos, que também tem 60 dentes e nela, um pinhão de 10 dentes atua sobre a roda de horas, também com 60 dentes.

Cada conjunto roda/pinhão tem que ter o mesmo passo (leia os artigos sobre engrenagens). A roda dos segundos leva o pinhão que é par da roda intermediária.

Como já escrevi artigos sobre desenho de engrenagens e redução (inclusive tem vídeo no canal DobitAoByteBrasil), vou passar apenas o macete para o cálculo do relógio Galileu, do trem de engrenagens, que é a espinha dorsal de um relógio.

Supondo que a escolha tenha sido 3600 batidas por hora e uma roda de segundos leve 1 minuto para rodar, tendo 30 dentes. Quanto maior for o pinhão, maior será a roda que faz par com ele. Quanto menor for o pinhão, maior será o esforço sobre ele. Um número de dentes bom para o pinhão varia entre 8 e 12. Supondo pinhões de 8 dentes, poderíamos então iniciar o cálculo do número de dentes necessários para as engrenagens:

calculo_das_engrenagens.webp

Isso porque o cálculo é feito sobre as duas rodas do tempo, afim de conseguir controlar os minutos. Então, sabendo que o pinhão desejado é de 8 dentes, usamos:

64/8 * (n)/8 * 30(2) = 3600

Resta descobrir o número de dentes para a segunda roda. Estou usando o exemplo do cálculo mais utilizado em relógios, por isso eu já sabia o valor, estou tentando apenas explicar o processo, caso deseje fazer engrenagens para um relógio com variações de batidas por hora ou número de dentes. A solução dessa equação é:

64/8 * 60/8 * 30(2) = 3600
8    *  7,5  * 60   = 3600

O resultado da divisão dá os números que, fazendo a multiplicação, dá 3600. Portanto, devemos criar 2 conjuntos de rodas e uma roda de escape, sendo:

  • Engrenagem de 64 dentes e pinhão de 8.
  • Engrenagem de 60 dentes e pinhão de 8
  • Engrenagem de 30 dentes

O formato do dente pode variar à vontade, exceto para os pares, porque o pinhão da roda de 64 dentes moverá a roda de 64 dentes e nos artigos relacionados expliquei que o conjunto deve ter o mesmo passo.

Leitura das horas no estilo Galileu

Invés de movimentar os ponteiros, a correspondência do tempo é lida diretamente nas engrenagens e os ponteiros são fixos nos eixos. As rodas se movem sobre os eixos, exceto a roda dos segundos, que nesse modelo criado por mim, está sobre o eixo do motor. Da esquerda para a direita, vemo na imagem de destaque a representação de hora, na roda maior. Acima dela está a roda dos minutos, que também contabiliza 2 horas por volta. No canto direito, enfim, a roda dos segundos, sendo a menor delas.

Como a roda dos segundos está sobre o eixo do motor, o ponteiro dos segundos foi fixado sobre a roda intermediária, que vai entre a escape wheel e a third wheel. A roda das horas usada nesse relógio é na verdade a wind wheel, mas o projeto Genesis foi criado para ser o mecanismo mais simples de todos os relógios.

Como fazer um relógio Cuco

O trabalho maior foi desenho do relógio, uma vez que as engrenagens são rápidas de desenhar; desenhei ponteiros bonitos (bem, eu achei bonito), uma caixa diminuta, uma fachada moderna (porque ninguém merece um cuco com um relevo de pássaro e um cacho de uvas) e bastante detalhes, inclusive artísticos. Nada foi desenhado por acaso nesse relógio:

translation-300x121.webp

Essa parte próxima ao topo tem dupla representação. Primeiro, 2 ponteiros indicando hora e minutos. A segunda, o circulo da esquerda passa a ser o Sol e o ponteiro da hora representa translação. O circulo central representa a Terra fazendo o movimento de translação e o circulo maior contém a lua e suas fases. Nessa segunda visão, o ponteiro de minutos representa a rotação. Eu quis representar o tempo nesse design.

Antes de formar uma opinião, considere que fazer em CAD não é a mesma coisa que desenhar no Photoshop. Utilizei o QCAD para desenhar o relógio e todos os elementos são feitos à base de cálculos. Além disso, não sou artista e isso é um trabalho maker, feito com muito orgulho.

Outra coisa que gostaria de citar antes de seguir adiante é que acabei aprendendo que para corte a laser é necessário que as peças desenhadas sejam polilinhas para que a máquina de corte não fique pulando para todo o canto invés de fazer um corte linear. Além de poupar tempo, isso ajuda a evitar marcas de queimado na madeira. Fiz alguns videos de desenho em CAD, disponíveis no  nosso canal no Youtube, não deixem de comentar.

cuco-qcad.png

Esse é o conjunto completo de peças para montar o relógio Cuco, só para ter uma ideia. E por falar em Cuco, vamos falar do passarinho.

Automata - Pássaro do relógio Cuco

Automatas são mecanismos de repetição cíclica que imitam um ou mais movimentos, normalmente de pessoas ou animais.  No caso do relógio Cuco, muitos são apenas um objeto estático que sai e entra da casinha, mas eu queria um pouco mais de movimento. Só que se eu fizesse o movimento dele por meio eletrônico, não seria tão legal, nem tão pequeno. Resolvi criar um pássaro que ao sair, abrisse o bico e levantasse as costas de leve, no estilo automata. Para fazê-lo sair da casinha, utilizei um servo-motor com engrenagem e uma cremalheira conectada ao pé do pássaro, o restante é feito de forma mecânica. Até que o conjunto ficou bem reduzido:

cuco-passaro-300x205.webp

São duas faces e o bico ao centro. Aquela parte que parece um papa-léguas é o bico e a costela. O prolongamento é para fazer um pêndulo. O círculo no pé dessa peça se desloca para cima quando chega no ponto alto da base. Depois, graças ao pêndulo, o bico se fecha no retorno do Cuco. O eixo está no olho. Parece simples, mas todo o deslocamento foi calculado; o avanço necessário, o número de dentes da engrenagem e cremalheira, a altura máxima e comprimento do Cuco e por fim, altura do conjunto motor em relação ao "piso" do passarinho.

Movimento das engrenagens do relógio Cuco

O movimento se dá por um motor de passo Nema 17, e não por uma força perpendicular, como é o caso do relógio de pêndulo tradicional. Mas em breve concluo um segundo projeto; um relógio de pêndulo eletromagnético. O motor de passo escolhido é excelente para esse projeto, pois tem 200 passos em full step, que dá um deslocamento de 1.8 graus com possibilidade variação máxima de 5%. Peguei o datasheet do motor para tirar as medidas, mas desconfiei de 2 valores. Por alguma razão, uma das medidas estava diferente, então resolvi desenhar o motor inteiro para obter as medidas reais. Vou deixar o desenho, caso precise de referência, mas tenha em mente que o datasheet deve ser procurado conforme o modelo do motor. Esse que tenho aqui é o SM-42BYG011-25. Alguns coincidem, outros não. Sua força não deve representar muito nesse relógio, porque o de pêndulo antigo que fiz eu consigo mover a roda soprando os dentes da engrenagem, mas para efeito de informação, esse motor tem 1.7kf.cm.

nema17-measurement.png

Como a parte mais importante é o trem de rodas do relógio, não pode haver 1mm sequer de diferença no posicionamento dos eixos (ainda mais considerando que quanto menor forem as engrenagens, menor será a tolerância), por isso foi fundamental ter a medida precisa do motor para fazer seu posicionamento na fachada do relógio Cuco. A medida que estava errada no datasheet era a de profundidade, não influenciaria em nada, mas fiquei com receio em considerar como certo as demais medidas depois disso.

datasheet do Nema 17SM-42BYG011-25.pdf.

Registros nas peças do relógio Cuco

A maioria das peças precisará de cola. Em todo o projeto não foi utilizado 1 parafuso sequer (exceto o motor, que daria pra fazer sem, mas preferi garantir por causa do peso) e todas as peças de posicionamento relevantes tem registros de encaixe. De qualquer modo, deve-se tomar todo o cuidado ao montar e garantir que ao menos o conjunto de engrenagens não tenha variação. Por fim, os eixos são de latão, comprados em casa de cutelaria. Mostro no vídeo o processo de polimento para deixá-lo mais liso e assim reduzir o atrito, mas sequer é necessário. Para polir, basta prender o eixo em uma furadeira e pressioná-lo com dois dedos, utilizando um pedaço de couro cru.

Eletrônica do relógio Cuco

Primeiro, permita-me dizer que um relógio Cuco novo é algo consideravelmente caro. Esse relógio sai uma pechincha em relação a um relógio Cuco tradicional, mas considerando como projeto DIY, não é barato como um quadrupede.

Se quiser economizar, desconsidere som; já economiza no falante e no player. Desconsidere iluminação; já economiza na controladora e nos LEDs. Desconsidere a movimentação do pássaro; economiza com servo motor. Se fizer um bom controle do motor de passo, dificilmente precisará ajustar a hora. O relógio ficará simples, mas funcional.

O eixo de latão é insubstituível, não queira usar prego no lugar. O eixo de latão é perfeitamente redondo, extraordinariamente barato e não enferruja.

O conjunto eletrônico é composto por:

Minha sugestão para esse projeto é a aquisição do material com o parceiro CurtoCircuito. Já deu pra ver que o projeto não é para crianças (por causa do custo), mas uma criança pode montar. O relógio de pêndulo ficará mais em conta, terá um circuito mínimo e aí sim, dará um bom projeto escolar. Na indicação de ESP32 tem também o link para o ESP32 Wemos do parceiro MASUGUX, cuja placa escrevi um artigo anteriormente.

A função dos LEDs RGB é imitar desde a alvorada ao crepúsculo, indo do laranja avermelhado ao branco amarelado para o amanhecer, depois descendo como o por do sol, com um branco de baixa luminosidade durante a noite, para imitar a iluminação da Lua.

O DFPlayer reproduzirá o som do Cuco mixado com o som do mecanismo. Poderia ser feito apenas o som de Cuco com um buzzer, mas nosso parceiro Curto, no carrossel sugeriu que o relógio tem um nível muito bom pra utilizar um recurso pobre como o buzzer. Para não esquentar o motor nunca, escolhi movimentar o motor a cada 2 minutos. Para que não pareça que o relógio está parado, coloquei em loop o som dos "tics" e "tacs". Isso ajudará saber também se o relógio está funcionando ou se teve um problema. Quando ligado, ele executa um "cuco" e faz o primeiro movimento de 2 minutos. Com isso, sabemos que todo o relógio está funcional.

O servo motor moverá o pássaro - mas em uma próxima versão, porque eu havia feito todo o projeto e distraidamente coloquei um enfeite no desenho da fachada, o que acabou consumindo 4 preciosos milímetros, por onde correria a cremalheira do pássaro.

Ao motor estará conectado um pinhão de 10 dentes e ao pássaro estará conectado uma cremalheira, como supracitado. O servo motor tem um movimento limitado a 180 graus, mas já feitas as medidas do deslocamento (menor que 3cm), apenas 4 dentes da cremalheira deverão se deslocar. O diâmetro do pinhão e o passo permitem um deslocamento bem maior do que o necessário.

O objetivo inicial era fazer o ajuste de horas pela Internet, por essa razão escrevi o artigo para configurar a hora no ESP32 por NTP, mas para isso seria necessário adicionar mais componentes e mais código. Antes que haja algum questionamento, sim, daria para fazer o motor girar mais rápido, mas não estamos falando de uma caixa de câmbio, são engrenagens de relógio.

Outro recurso interessante é a definição dos horários de toque. Um relógio Cuco convencional toca 1 vez a cada meia hora e N vezes correspondente ao número de horas inteiras. O problema é que ele não é discreto. Quando for meia-noite, ele tocará com o mesmo vigor de meio-dia. Para evitar essa condição, é possível ajustar o período de toque, por exemplo, das 6 da manhã até às 20:00. Além disso, é possível definir um alarme, selecionando hora do alarme e número de toques. Desse modo, quem gosta de acordar em intervalo quebrado, como por exemplo, 6:45, poderia também utilizar o relógio cuco como despertador. Essa parte de alarme ainda não implementei no código. O farei, mas o próximo artigo do cuco será com uma simplificação extrema para derrubar o custo de montagem de forma que seja atrativo para qualquer maker.

Já me adianto - não se assuste com a eletrônica - basta ligar conforme especificado no código. São apenas 5 pinos de IO do ESP32, graças ao barramento I2C. Se olhar bem pro restante, a maioria é VCC e GND (9 VCC 5V e 1VCC 3v3, com 7GND dos 5V). Além dos 5 GPIO, tem 2 fios que vão para o speaker. Os LEDs, claro, tem os fios RGB e VCC, mas estão alí nos pinos do PCA. O PCA9685 e o PCA8574 são I2C, basta interligá-los. Pronto!

cuco-circuit.webp

Primeiro passo - Montar o relógio Cuco

Agora é hora de por a mão na massa. No primeiro momento, podemos montar o pássaro,  o mecanismo do pássaro e os pinhões às engrenagens. Tudo tem técnica, por isso é melhor assistir aos videos na playlist Relógio Cuco no nosso canal do Youtube (pois é, achei melhor separar as etapas em diversos videos). O pássaro não tem segredo pra funcionar, basta mover 3 dentes da cremalheira - mas, como errei a porta por 4mm, não pude movê-lo.

Sugiro que aprecie a leitura, posteriormente assista à playlist que estará disponível nesse link.

Programação

O código da versão atual (enquanto estou testando) está disponível com todas as funcionalidades dispostas no sketch principal.

Normalmente eu utilizava o PlatformIO com a board NodeMCU-32S, a ser programado com o CodeBlocks.

platformio init --ide codeblocks --board nodemcu-32s

Só que dessa vez utilizei o Atom com PlatformIO e Clang para autocompletar. Se desejar programar em outra IDE que não a do Arduino ou CodeBlocks, sugiro esse artigo.

ESP32 WROOM

Já existem diversos modelos do ESP32 e cada qual deve ser utilizado para seu propósito. Não faz sentido utilizar um ESP32 com display, sendo que ficará dentro do relógio. Além do mais, seu tamanho é desconfortável para esse projeto.

Ele funcionará no modo AP e Station, para que seja fácil a qualquer um reconfigurá-lo em uma LAN doméstica. Ou ainda, caso haja alguma alteração em sua rede WiFi, para que não seja necessário desmontar o relógio para subir um novo firmware. Só que não coloquei o servidor web ainda nessa versão do código. Caso queira fazê-lo, veja esse outro artigo, onde coloquei MQTT e webserver juntos no ESP32.

Estou pensando em adicionar também o recurso de OTA à biblioteca, mas é melhor dar um passo por vez. O projeto do relógio me tomou 240 horas e a programação levou mais de 50 horas por causa dos testes, wiring, montagem da placa etc. Além disso, a pintura levou mais algumas horas, e teve também os testes prévios em madeira para registrar o procedimento adotado. Claro, se eu for montar outro agora, não levo mais de 4 horas. Se você for montar um, deve passar um sábado e domingo montando, mas só por ser o primeiro também.

ESP32 WROOM pinout

Para nos situarmos, vamos utilizar esse "mapa" da placa.

esp32-pinout.webp

ESP32 Wemos Mini

Já para o ESP32 Wemos Mini, basta olhar agora na traseira da placa:

esp32_wemos-pinout-248x300.webp

Wiring da parte eletrônica

Tem um bocadinho de coisas, vou tentar ser bem claro em cada parte. Sugiro que se for montar esse circuito todo, teste as partes de forma isolada e depois faça a integração, para facilitar a depuração de erros.

Ainda existe outra opção para a iluminação, que seria utilizar LEDs RGB endereçáveis invés de LEDs RGB tradicionais. Mas como tenho que fazer o controle do pássaro também, achei melhor utilizar a PCA9685 mesmo. Leia esse artigo, caso deseje utilizar LEDs endereçáveis invés do tradicional. No projeto eu já coloquei os encaixes para o LED RGB comum - e por falar em comum, se for utilizar o PCA9685, lembre-se que o LED RGB precisa ser anodo comum, porque o aterramento é feito nos pinos dessa board.

Tanto o PCA9685 como o PCF8574 são I2C, portanto um deles irá conectado aos pinos de GPIO e o outro interligado na primeira placa. Estou optando por deixar o PCA9685 conectado diretamente aos pinos de GPIO, mas é por acaso. Tanto faz, uma vez que para fazer a comunicação com o barramento I2C se faz necessário passar o endereço da respectiva placa.

Olhando o todo, assusta um pouco, mas se fizer 1 por 1 e testar a cada etapa, vai perceber que é simples. Sugestão:

  • Subir o sketch no ESP32.
  • Conectar o PCA9685 e os LEDs e testar.
  • Conectar o PCF8574, EasyDriver e o motor de passo (com a fonte) e testar.
  • Colocar o DFPlayer com o falante e testar.

Só quero salientar que a serial deve ser conectado TX do ESP32 ao RX do DFPlayer e RX do ESP32 ao TX do DFPLayer. O conceito é simples, "T" de Transmitir e "R" de Receber.

Atenção ao motor de passo

Se ligar a fonte e o EasyDriver sem ligar o PCF8574, o motor vai chegar rapidinho a 70 graus. Recomendo que só ligue a fonte de alimentação quando o ESP32 já estiver ligado, assim a configuração inicial dos pinos será executada e o motor estará em modo sleep.

Código

O código não ficou lá muito pequeno e nem está limpo, porque tive que escrever diversos testes durante o desenvolvimento. Posteriormente faço uma outra versão do código pra simplificar e coloco no github do site. Mas sério, vou fazer uma versão simplificada, só persisti nessa porque é para mim mesmo.

Ainda falta implementar um monte de recursos; OTA, Blinky, talvez um RTC, um sensor de temperatura e umidade, um display OLED...

#include <Arduino.h>
#include <PCA9685.h>
#include <Wire.h>
#include <WiFi.h>
#include <HardwareSerial.h>
#include "freertos/FreeRTOS.h"
#include "esp_wifi.h"
#include "esp_wpa2.h"
#include "time.h"
#include "esp_event_loop.h"
#include "freertos/task.h"
//#include "freertos/queue.h"
#//include "freertos/semphr.h"
#include "freertos/event_groups.h"
#include "DFRobotDFPlayerMini.h"

//pinos escolhidos para o i2c
#define SDA      18
#define SCL      19
//pinos escolhidos para Serial
#define S1RX     32
#define S1TX     21
//pino para interrupção da posição da engrenagem de segundos
//quando mudar o projeto pra motor DC
#define GEAR_PIN 17

//endereço da placa
#define PCA9685_BASEADR      0x70
#define PCF8574_MOTOR_REMV   0x24 //pcf de teste Remover
#define PCA9685_MOTOR_ADDR   0x24

//girar o motor na direção horária
#define CLOCKWISE 1

//Horário limite para tocar o som do cuco
#define TIME_LIMIT_TO_PLAY 22
//começa a tocar somente depois desse horário
#define STARTS_PLAY_AFTER 6
//Para o posicionamento correto foi necessário rodar 196 passos,
//dos 200 por volta.
#define MOTOR_STEPS_TO_RUN 196

TaskHandle_t ticTacTask;

TaskHandle_t gearPosTask;

//Credenciais WiFi
const char* SSID   =  "SuhankoFamily";
const char* PASSWD = "fsjmr112";

//Configurações do serviço NTP para busca de hora online
const char* ntpServer          = "pool.ntp.org";
const long  gmtOffset_sec      = 0;
const int   daylightOffset_sec = -3600*3; //-3, desconsiderando horário de verão.

char init_pos = 0;
char end_pos  = 0;
String turn_on_in;
String turn_off_in;

char linebuf[80]; //variavel do client
char minutesToRun = 0; //guarda o valor de voltas a dar

int charcount = 0;

int TIME_TO_SLEEP = 60*1000*2; // 0, não roda nunca. 120000 = 2 minutos, etc.

//aloca memória para guardar o valor vindo do usuário (debug)
char *valueFromUser = (char *) malloc(sizeof(char)*3);
//pega valor para acionamento pela serial (debug).
char number[6] = {0};

//pin_stat guarda os estados dos pinos do PCF8574
unsigned char pin_stat = 0;

bool dontRun = false; //a task ou usuario nao executam o motor se estiver em uso.
//permite (ou não) o som do tic-tac. Estado inicial: sim, começa tocando.
bool tictac = true;
//inicia o som do cuco quando o ESP32 fizer o boot. Essa variável vira true no loop()
bool started = false;
//flag para iniciar o som de movimento das engrenagens (pode ser irritante)
bool movingGears = false;

bool stop_now = false;

byte pinGearStat = 0;

//controle do motor, enumerando os pinos do PCF8574
enum pinMap{
  motor_step,
  motor_direction,
  motor_sleep,
  motor_ms1,
  motor_enable,
  motor_ms2
};

//instância da PCA9685. Os parâmetros são endereço, tipo de controle e frequência
PCA9685 driver = PCA9685(PCA9685_BASEADR, PCA9685_MODE_LED_DIRECT, 800.0);

//Serial utilizada para comunicação com o DFRobotDFPlayerMini
HardwareSerial SecondSerial(2);

//Objeto para controlar o DFPlayer
DFRobotDFPlayerMini myDFPlayer;

//cockcrow canto do galo
// função para controlar os LEDs RGB
void ledControl(byte led_number);

void vTaskTicTac(void *pvParameters);

//move o motor de passo
void moveMotor(int times);

void cuckooSing(byte times);

//variável alocada para copia do array correspondente à iluminação, conforme a hora do dia
/* Essa variável é um ponteiro para pegar um dos valores do array abaixo, conforme a hora
do dia*/
int *weather;

byte myTime       = 0; //TODO: criar um timer para acumular horas
byte minute       = 0; //TODO: criar um timer para acumular horas
byte latestMinute = 0;

//estrutura para armazenar as cores que correspondam à iluminação conforme a hora do dia
struct schemes{
  int dawn[3]        = {500,200,200};
  int morning[3]     = {0,3000,3000};
  int midday[3]      = {3000,2000,4000};
  int afternoon[3]   = {3000,1500,1500};
  int almostNight[3] = {1000,200,400};
  int twilight[3]    = {50,50,50};
};
struct schemes schemesValues;

void cuckooSing(byte times){
  Serial.print("Cuckoo: ");
  Serial.println(times);
  vTaskDelete(ticTacTask);
  for (byte i=0;i<times;i++){
      myDFPlayer.stop();
      myDFPlayer.volume(30);
      //toca o cuco
      myDFPlayer.play(1);  //1 "cucada" da meia hora.
      vTaskDelay(pdMS_TO_TICKS(2555));
      myDFPlayer.stop();
  }
  xTaskCreate(vTaskTicTac,"vTaskTicTac",10000,NULL,0,&ticTacTask);
}

//busca na internet a hora e minuto para fazer os controles
//TODO: tem que garantir os valores, pois pode haver um erro de conexão e
//só isso já será o suficiente para causar um reboot.
void printLocalTime()
{
  struct tm timeinfo;
  if(!getLocalTime(&timeinfo)){
    Serial.println("Failed to obtain time");
    vTaskDelay(pdMS_TO_TICKS(1000));
    if(!getLocalTime(&timeinfo)){
      Serial.println("Falha de conexao?");
    }
  }
  myTime = timeinfo.tm_hour;
  minute = timeinfo.tm_min;
  Serial.print("DEBUG: ");
  Serial.print(timeinfo.tm_hour);
  Serial.print(" ");
  Serial.println(timeinfo.tm_min);
}


/* Essa task fica verificando em intervalos de 10ms o estado do pino IO17. Se
 ocorrer a interrupção, levanta a flag stop_now, ida dentro da função moveMotor,
 no loop que movimenta o motor. Na função moveMotor, quando essa flag for
 percebida, deverá suspender a task vTaskGearPos e baixar a flag. Só levanta
 novamente após 100 passos. ATENCAO: testar primeiro digitalRead direto na funcao!!!!!!
 */
 void vTaskGearPos(void *pvParameters){
   while (true){
       pinGearStat = digitalRead(GEAR_PIN);
       if (pinGearStat == 1){
           stop_now = true;
       }
       vTaskDelay(pdMS_TO_TICKS(10));
   }
 }

//Essa é a task que mais trabalha. Ela mantém o play do som de tic-tac.
/*Seria possível controlar o player pegando feedback do pino busy, mas para
não haver delay, preferi usar o tempo real do áudio.*/
//(task passiva)
void vTaskTicTac(void *pvParameters){
  myDFPlayer.stop();
  vTaskDelay(pdMS_TO_TICKS(10));
  myDFPlayer.volume(30);
  vTaskDelay(pdMS_TO_TICKS(10));
  myDFPlayer.loop(2);
  while (true){
        vTaskDelay(pdMS_TO_TICKS(10));
  }
}

//tarefa encarregada do ajuste dos LEDs. Ela executa a função responsável por
//buscar a hora na internet e alimentar as variáveis de hora e minuto. Depois,
//chama a função que configura os LEDs RGB (até 5 possíveis).
//(task ativa)
void vTaskLightControl(void *pvParameters){
  delay(1000);
  cuckooSing(1);
  configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);

  for (byte i=0;i<16;i++){
      driver.getPin(i).setValueAndWrite(0);
      vTaskDelay(pdMS_TO_TICKS(10));
  }
  while (true){
      printLocalTime();
      ledControl(0);
      ledControl(1);

      if (minute == 30){
        if (myTime < TIME_LIMIT_TO_PLAY && myTime > STARTS_PLAY_AFTER){
            cuckooSing(1);
            while (minute == 30){
                printLocalTime();
                vTaskDelay(pdMS_TO_TICKS(3000));
            }
        }
      }
      //o mesmo procedimento para hora inteira
      else if (minute == 0){
        byte hour = myTime > 12 ? myTime-12 : myTime;
        //Não encher o saco na hora de dormir. Limite de trabalho do cuco.
        if (myTime < TIME_LIMIT_TO_PLAY && myTime > STARTS_PLAY_AFTER){
            cuckooSing(hour);
            while (minute == 0){
                vTaskDelay(pdMS_TO_TICKS(1000));
                printLocalTime();
                Serial.println("minuto 0");
            }
        }
      }

      vTaskDelay(pdMS_TO_TICKS(60000));
      Serial.println("DEBUG: vTaskLightControl (1m)");
  }
}

//task para rodar o motor periodicamente
/* Se não leu os artigos de ESP32 relacionados às tasks, sugiro a leitura.
Toda task não tem retorno e deve receber o parâmetro void.*/
void vTaskRunMotor(void *pvParameters){
    unsigned long current;
    unsigned long interval;

    /* A task é um processo assíncrono, portanto não segue o fluxo da função loop(). Para evitar colisão com um ajuste de minutos,
    a variável booleana dontRun não permite que ela seja executada. A partir de então a task fica monitorando a cada 1 segundo
    para saber quando poderá voltar ao fluxo normal. Quando isso ocorrer, significa que o ajuste ACABOU de ser feito, portanto
    a task vará um ciclo completo de espera para retomar a rotina. O boolean waitFullCycle faz o sleep da task pelo tempo determinado
    na variável TIME_TO_SLEEP.
    Efeito colateral: Repare que quando o motor é movido pela task, ela considera a diferença entre o início do movimento e o tempo de
    espera, de modo que o relógio não atrasará. Porém, ao executar o ajuste pelo usuário, o tempo que levou para fazer o ajuste é perdido.
    TODO: criar um controle desse tempo para que seja GLOBAL.
    De qualquer modo, a melhor opção é fazer o ajuste como explicado no artigo.
    */

    bool waitFullCycle = false; // se entrar em dontRun, significa que foi mexido pelo usuario e deve aguardar o ciclo inteiro
    for (;;){
        while (TIME_TO_SLEEP < 120000){ //não é bom que seja menor que 2 minutos porque o motor vai esquentar se ficar funcionando continuamente
            vTaskDelay(pdMS_TO_TICKS(1000));
        }
        while (dontRun){
            vTaskDelay(pdMS_TO_TICKS(1000));
            waitFullCycle = true;
        }
        if (waitFullCycle){
            vTaskDelay(pdMS_TO_TICKS(TIME_TO_SLEEP)); // acabou de ser ajustado pelo usuario, então aguarda 2 minutos
            waitFullCycle = false;  // desabilita a flag, agora é interação normal
        }
        //move o motor, compensando a diferença de tempo entre o tempo de mover e o intervalo
        interval = millis();
        moveMotor(MOTOR_STEPS_TO_RUN);
        current = TIME_TO_SLEEP - (millis()-interval);
        vTaskDelay(pdMS_TO_TICKS(current)); //2 minutos, considerando o tempo tomado pelo movimento do motor.
    }
}

//funcão para testar os bits do PCF8574
void pcf8574Test(){
  unsigned char pin_stat_test = 0;
  for (byte i=0;i<8;i++){
    Wire.beginTransmission(PCF8574_MOTOR_REMV);
    Wire.write(pin_stat_test);
    Wire.endTransmission();
    pin_stat_test = 1<<i;
    vTaskDelay(pdMS_TO_TICKS(1000));
  }
}

//seleção da velocidade do motor
void motorSpeed(){
  //full step
    pin_stat = pin_stat&~(1<<motor_ms1);
    pin_stat = pin_stat&~(1<<motor_ms2);
}

//Desativa o motor para não esquentar
void do_sleep(){
    //when sleeping, the motor doesn't gets hot
    pin_stat = pin_stat&~(1<<motor_sleep);
    Wire.beginTransmission(PCF8574_MOTOR_REMV);
    Wire.write(pin_stat);
    Wire.endTransmission();
}

//todos os pinos do PCF8574 baixos
void turnOff(){
    pin_stat = 0;
    motorSpeed();
    Wire.beginTransmission(PCF8574_MOTOR_REMV);
    Wire.write(pin_stat);
    Wire.endTransmission();
}

//configuração inicial do motor
void motorSetup(){
  motorSpeed();
  pin_stat = pin_stat&~1<<motor_direction; //add a bit on motor direction position
  pin_stat = pin_stat&~(1<<motor_sleep); //sleep 0
  pin_stat = pin_stat&~(1<<motor_ms1);
  pin_stat = pin_stat&~(motor_ms2);
  pin_stat = pin_stat|(1<<motor_enable); // enable 1
  Wire.beginTransmission(PCF8574_MOTOR_REMV);
  Wire.write(pin_stat);
  Wire.endTransmission();

  pin_stat = pin_stat&~(1<<motor_sleep); //sleep 0
  pin_stat = pin_stat|(1<<motor_enable); // enable 1
  Wire.beginTransmission(PCF8574_MOTOR_REMV);
  Wire.write(pin_stat);
  Wire.endTransmission();
}

//move o motor
void moveMotor(int times){
  pin_stat = pin_stat|(1<<motor_sleep); //sleep 0
  pin_stat = pin_stat|(1<<motor_enable); // enable 1
  Wire.beginTransmission(PCF8574_MOTOR_REMV);
  Wire.write(pin_stat);
  Wire.endTransmission();

  for (int count = 0; count <times; count++){
      pinGearStat = digitalRead(GEAR_PIN);
      /*
      if (pinGearStat == 1 && count > 10){
          Serial.println("pinGearStat");
          Serial.println(pinGearStat);
          break;
      }*/

      pin_stat = pin_stat|(1<<motor_step);
      Wire.beginTransmission(PCF8574_MOTOR_REMV);
      Wire.write(pin_stat);
      Wire.endTransmission();

      delay(1);//20

      pin_stat = pin_stat&~(1<<motor_step);
      Wire.beginTransmission(PCF8574_MOTOR_REMV);
      Wire.write(pin_stat);
      Wire.endTransmission();
      delay(2);//5
      Serial.print("step: ");
      Serial.println(count);
  }

    pin_stat = pin_stat&~(1<<motor_sleep); //sleep 0
    pin_stat = pin_stat|(1<<motor_enable); // enable 1
    Wire.beginTransmission(PCF8574_MOTOR_REMV);
    Wire.write(pin_stat);
    Wire.endTransmission();
}

/* Os pinos do PCA9685 são controlados de 3 em 3, R, G e B. Basta passar o
número do LED (de 1 a 5) para que a posição respectiva seja configurada conforme
a hora do dia.
*/
void ledControl(byte led_number){
  //como free() está com bug, não posso criar a cada chamada, então
  //deixo global e só reatribuo o valor
    //int *weather = (int*) malloc(sizeof(int)*3);
    byte max = led_number*3; // desse modo configuro o array nos pinos corretos
    /*as condicionais abaixo podem ser modificadas como quiser. Eu selecionei
    aproximadamente os horários em que a luz pode variar mais. A variavel
    myTime guardará as horas por intermédio de uma task (que será criada) a
    posteriori. Conforme o horário, os valores virão com as cores (ainda a ajustar)
    que representem aproximadamente a luz do respectivo horário.*/
    if (myTime > 0 && myTime < 6){
        weather = schemesValues.dawn;
    }
    else if (myTime >5 && myTime < 11){
        weather = schemesValues.morning;
    }
    else if (myTime >10 && myTime < 16){
        weather = schemesValues.midday;
    }
    else if (myTime >15 && myTime < 17){
      weather = schemesValues.afternoon;
    }
    else if (myTime >16 && myTime < 19){
      weather = schemesValues.almostNight;
    }
    else{
      weather = schemesValues.twilight;
    }
    //Tendo determinado o array de cores a utilizar, agora atribui-se aos pinos.
    for (byte i=0;i<3;i++){
      driver.getPin(max).setValueAndWrite(weather[i]);
      //Serial.println(weather[i]);
      delay(100);
      max++;
    }
    /* Como free() deu um bug, tive que criar a variável weather global e apenas
    zerar os valores após o uso.*/
    //memset(&weather, 0, 3);
    //free está dando erro no esp32
    //free(weather);
}

void setup() {
    // Serial para debug
    Serial.begin(115200);

    //configuração do pino do reedSwitch
    pinMode(GEAR_PIN,INPUT_PULLDOWN);

    //Serial para comunicação com DFPlayer. TEM que ser 9600.
    SecondSerial.begin(9600,SERIAL_8N1,S1RX,S1TX);

    //Inicializa o módulo MP3
    if (!myDFPlayer.begin(SecondSerial)){
        byte counter = 0;
        Serial.print("Waiting for serial initializing.");
        for (counter=0; counter <20; counter++){
            Serial.print(".");
            vTaskDelay(pdMS_TO_TICKS(100));
        }
    }

    //soft ap configurará por padrão o IP 192.168.4.1
    WiFi.mode(WIFI_MODE_APSTA);
    WiFi.setHostname("CrazyCuckoo");
    WiFi.softAPsetHostname("CrazyCuckoo");
    WiFi.softAP("CrazyCuckoo");
    WiFi.begin(SSID, PASSWD);
    Serial.print("\nConnecting.");
    while (WiFi.status() != WL_CONNECTED) {
      delay(500);
      Serial.print(".");
      Serial.println(WiFi.status());
    }
    Serial.println("\n");
    Serial.print(" Hostname: ");
    Serial.println(WiFi.getHostname());
    Serial.print("Mode: ");
    Serial.println(WiFi.getMode());
    Serial.print("TX power: ");
    Serial.println(WiFi.getTxPower());

    // inicializa o i2c
    Wire.begin(SDA,SCL);

    // inicializa a PCA9685
    driver.setup();
    //desliga todos os pinos da PCA9685
    for (byte i=0;i<16;i++){
        driver.getPin(i).setValueAndWrite(0);
    }

    //configura o motor
    motorSetup();
    //faz um "wake up"
    //moveMotor(1);
    //zera a variável number
    memset(number,0,sizeof(number));

    //vTaskRunMotor
    xTaskCreatePinnedToCore(vTaskLightControl,"vTaskLightControl",10000,NULL,20,NULL,0);
    //vTaskLightControl
    xTaskCreatePinnedToCore(vTaskRunMotor,"vTaskRunMotor",10000,NULL,10,NULL,0);
    //vTaskTicTac
    xTaskCreatePinnedToCore(vTaskTicTac,"vTaskTicTac",10000,NULL,0,&ticTacTask,0);
}

void loop() {
    vTaskDelay(pdMS_TO_TICKS(10));
    //Monitora a serial. Se tiver comando para o motor, o move.


    if (Serial.available()){
        Serial.readBytesUntil('\n',number,5);
    }
    //int val = atoi(number);

    if (number[0] != 48 && number[0] != 0){
        Serial.print("ok: ");
        Serial.println(atoi(number));
        cuckooSing(atoi(number));
        memset(number,0,sizeof(number));
    }
}

Vídeo

O resultado desse enorme trabalho pode ser contemplado no nosso canal DobitAoByteBrasil no Youtube. O trabalho foi árduo, então se puder, retribua se inscrevendo no nosso canal e deixando seu like para motivar novos artigos, ok? Se você chegou até essa linha, obrigado pelo interesse, sei que um artigo desse tamanho não é leitura para qualquer um.

Não

Como eu sei que despertará o interesse em muitas pessoas, deixei o máximo de detalhes possível para que com dedicação, a pessoa interessada possa também criar seu próprio relógio. Passei 3 meses desenvolvendo, testando mecânica, testando eletrônica, escrevendo artigos relacionados, fazendo vídeos. Por essa razão, não vou fornecer o arquivo do meu projeto, não vou dar consultoria gratuita e nem paga, não vou tirar dúvidas. Por favor, estude o artigo e reproduza ou crie um novo. Sei que soa muito chato isso, mas já houve manifestações a respeito até em grupos internacionais e não posso tirar nem "dúvida simples", nem "rapidinho", senão viro suporte por tempo indeterminado.

Ainda não tem uma CNC laser?

Se está considerando comprar uma CNC laser, cuidado para não cair em armadilhas.

Minha sugestão é a CNC laser da ECNC, que em seu modelo de saída já é um espetáculo, com dimensões de corte de 60x50 e tubo laser de 100W - a L-560.

Recomendo fortemente e aguarde, novos artigos virão! Ah, e comprando com a ECNC, você ganhará diversos projetos prontos para corte, inclusive alguns meus que estou desenvolvendo, como esse relógio modificado para uma eletrônica simples, que você poderá montar em casa!

Inscreva-se no nosso canal Manual do Maker no YouTube.

Também estamos no Instagram.

Nome do Autor

Djames Suhanko

Autor do blog "Do bit Ao Byte / Manual do Maker".

Viciado em embarcados desde 2006.
LinuxUser 158.760, desde 1997.