Manufaturação industrial
Internet das coisas industrial | Materiais industriais | Manutenção e reparo de equipamentos | Programação industrial |
home  MfgRobots >> Manufaturação industrial >  >> Manufacturing Technology >> Processo de manufatura

Relógio preciso usando apenas um Arduino

Componentes e suprimentos

Arduino Nano R3
Usei um Nano, mas deveria funcionar com qualquer Arduino
× 1
LCD alfanumérico, 16 x 2
Qualquer monitor deve funcionar, usei este https://www.amazon.co.uk/ gp / product / B00N8K2BYM / ref =ppx_yo_dt_b_asin_title_o02_s00? ie =UTF8 &psc =1
× 1
Interruptor tátil, acionado pela parte superior
× 3
Potenciômetro Trimmer, 10 kohm
Qualquer aparador de 10k serve
× 1
Jumper fios
× 1

Sobre este projeto


Comecei isso como um exercício acadêmico, mas acabei com um relógio muito preciso. Depois de correr por 5 dias, não havia perdido nem ganho tempo.

O principal problema de usar apenas um Arduino é que a velocidade do clock interno não é 100% preciso. Portanto, se você confiar apenas nisso, a contagem dos milissegundos decorridos será reduzida em uma pequena porcentagem e o relógio que você está criando perderá ou ganhará tempo. Minha abordagem foi testar a precisão do Arduino que eu estava usando e determinar quantos milissegundos ele perdeu ou ganhou por hora. Tudo o que foi necessário então foi programar um ajuste de velocidade para adicionar ou deduzir essa diferença dos milissegundos rastreados internamente a cada hora.

Minha outra preocupação era se o relógio do Arduino estava consistentemente impreciso, mas conforme indicado, o relógio que programei manteve a hora muito precisa por 5 dias, então parece que a imprecisão é consistente.

O segundo problema é que a função millis () interna é redefinida a cada 50 dias ou mais e você não pode manipular a contagem de milissegundos. Conseqüentemente, a resposta foi substituir a interrupção millis () por um contador que eu pudesse manipular e que contaria os milissegundos a partir da meia-noite, reiniciando a cada dia removendo quaisquer restrições de tempo de execução.





Avaliando a imprecisão


Para avaliar a imprecisão, presumi que o relógio do meu computador e, portanto, os milis () em Processing estava correto. Portanto, criei um programa para o Arduino para enviar o número de milissegundos decorridos desde o aperto de mão para o Processing uma vez a cada 2 segundos e um script para o Processing ler isso e compará-lo aos milissegundos decorridos exibindo um resultado em tempo real e a diferença após uma hora decorrido. Isso deu o número de milissegundos que foram perdidos ou ganhos em uma hora e, portanto, o valor a ser usado para o ajuste de velocidade no programa do relógio.

O código para o programa Arduino e o script de processamento são fornecidos abaixo.

Se você não tiver o Processing instalado, visite https://processing.org onde você pode fazer o download e aprender sobre ele.





O código do relógio


As principais áreas de interesse no código do relógio são a configuração da interrupção, como ela é usada e a maneira como a data é mantida e manipulada.

A interrupção

O código a seguir definirá uma interrupção que será acionada a cada milissegundo. Isso desvia a interrupção usada para manter millis () para que millis () e delay () não funcionem mais.
  // Configurar interrupção de tempo - millis () rola após 50 dias, então // estamos usando nosso próprio contador de milissegundos que podemos zerar // no final de cada dia // Definir o modo CTC Compare tempo e interrupção de disparo TCCR0A =(1 < 

Este é o código que será chamado a cada segundo:
  // Esta interrupção é chamada quando o tempo de comparação é atingido // portanto, será chamado uma vez por milissegundo com base na // configuração do registro OCR0A.ISR (TIMER0_COMPA_vect) {if (currentMode! =SET_TIME) currentTime ++; decorrido ++;}  

currentTime e elapsed são variáveis ​​longas sem sinal. Observe que eles são qualificados como voláteis quando definidos, pois também estamos manipulando as variáveis ​​no código principal. Isso força o sistema a ler a variável sempre que ela é usada e não usa um valor armazenado em cache.

currentTime armazena o número de milissegundos desde a meia-noite e existem rotinas para converter isso para HH:MM:SS e redefini-lo quando você definir a hora.

Após 24 horas, o sistema deduz o número de milissegundos em um dia da hora e aumenta a data em 1 dia. O relógio, portanto, não é impactado pelo valor máximo que a variável pode armazenar, ao contrário de millis ().
  // Se no final do dia reiniciar o tempo e aumentar a data if ((currentMode ==SHOW_TIME) &&(currentTime> millisecondsInADay)) {// Dia seguinte // Parar as interrupções durante o tempo de reinicialização noInterrupts (); currentTime - =milissegundosInADay; interrupções (); currentDate ++; }  

Observe que desabilitamos as interrupções enquanto manipulamos a variável currentTime, caso contrário, a chamada de interrupção poderia ser disparada no meio do cálculo para deduzir milissegundosInADay, corrompendo o cálculo.

Após cada hora ter passado, o sistema ajusta o número de milissegundos decorridos pelo ajuste de velocidade que calculamos anteriormente, ajustando a hora atual para compensar o relógio interno rápido ou lento.
  // No final de cada hora, ajuste o tempo decorrido para // a imprecisão no relógio do Arduino if (elapsed> =millisecondsInHour) {noInterrupts (); // Ajustar o tempo para o relógio do Arduino lento / rápido currentTime + =speedCorrection; // Reinicia para contar a próxima hora decorrida =0; interrupções (); }  

Armazenamento de dados e cálculo

A data é mantida como uma data juliana, que é o número de dias decorridos desde segunda-feira, 1º de janeiro de 4713 AC. Rotinas são incluídas para calcular a data juliana e convertê-la de volta para o calendário gregoriano.
  float JulianDate (int iday, int imonth, int iyear) {// Calcula a data juliana (testado até o ano 20.000) unsigned long d =iday; m longo sem sinal =mês; sem sinal longo y =iano; se (m <3) {m =m + 12; y =y - 1; } t1 longo sem sinal =(153 * m - 457) / 5; t2 longo sem sinal =365 * y + (y / 4) - (y / 100) + (y / 400); return 1721118.5 + d + t1 + t2;} void GregorianDate (float jd, int &iday, int &imonth, int &iyear) {// Nota 2100 é o próximo ano bissexto ignorado - compensa os anos bissextos ignorados sem sinal longo f =jd + 68569.5; e longo sem sinal =(4,0 * f) / 146097; g longo sem sinal =f - (146097 * e + 3) / 4; h longo sem sinal =4000ul * (g + 1) / 1461001; t longo sem sinal =g - (1461 * h / 4) + 31; u longo sem sinal =(80ul * t) / 2447; longo sem sinal v =u / 11; iano =100 * (e - 49) + h + v; mês imediatamente =u + 2 - 12 * v; iday =t - 2447 * u / 80;}  

Os botões de ajuste

O botão Mode avança o modo atual de Show Time, para Set Time, Set Year, Set Date, Set Speed ​​Adjustment e volta para Show Time. Cada um deles é autoexplicativo e use os outros 2 botões para ajustar a configuração atual.

Uma vez que o relógio está funcionando, se estiver ganhando ou perdendo tempo, você pode alterar o ajuste de velocidade, acessando o modo Set Speed ​​Adjustment e usando o botão para cima e para baixo para aumentar ou diminuir em 5 segundos de cada vez.

Código

  • Programa Relógio
  • programa de cronômetro do Arduino
  • Processando script de teste de cronômetro
Programa de relógio Arduino
Relógio preciso com data apenas usando e Arduino
 // Paul Brace - fevereiro 2021 // Relógio simples com data criado apenas usando um Arduino - sem módulo RTC // O programa incorpora um ajuste de correção de tempo para compensar a // velocidade do relógio interno não sendo 100% preciso.// Uma vez que o ajuste correto de velocidade é definido, o relógio é surpreendentemente preciso.// No meu teste, ele não perdeu ou ganhou nenhum tempo em um período de 5 dias.// Exibe a hora em um display LCD 16x2 // Botões para definir a hora // Botão de modo (pino 2) alterna definir hora, definir data e executar // Botão 1 (pino 3) Aumenta Minutos e Mês e diminui Ano / velocidade ajust // Botão 2 (pino 4) Aumenta Hora e Dia e aumenta Year./speed adj // Exibição de 24 horas // Inclui o driver da biblioteca para exibição:#include  // LiquidCrystal lcd (RS, EN, D4, D5, D6, D7) LiquidCrystal lcd (12, 13 , 6, 7, 8, 9); // cria um objeto lcd e atribui os pinos // Define botões e conexões de campainha # define MODE_BUTTON 2 # define HOUR_BUTTON 3 // Mesmo botão diferentes definições para # define UP_BUTTON 3 // torna o código mais fácil de entender #define DAY_BUTTON 3 # define MINUTE_BUTTON 4 // Mesmo botão diferentes definições para # define DOWN_BUTTON 4 // torna o código mais fácil de entender #define MONTH_BUTTON 4 // Configurações do modo atual # define SHOW_TIME 1 // 1 =executando - show time # define SET_TIME 2 // 2 =tempo definido # define SET_YEAR 3 // 3 =ano definido # define SET_DATE 4 // 4 =dia / mês definido # define SET_SPEED_ADJ 5 // 5 =altera a variável speedCorrectionint speedCorrection =3545; // Número de milissegundos em que meu relógio Nano funciona lento por hora // número negativo aqui se estiver executando rápido // mude para corresponder às suas variáveis ​​Arduino // Variáveis ​​voláteis alteradas em uma interrupção e // precisamos forçar o sistema a ler o variável real // quando usada fora da interrupção e não usa uma versão em cachevolatile unsigned long currentTime; // Duração em milissegundos de midnightunsigned long lastTime =-1000; // lastTime que ShowTime foi chamado inicializado com -1000, então mostra imediatamentevolátil sem sinal longo decorrido; // Timer usado para atraso e hora countunsigned long millisecondsInADay; // Milissegundos em 24 horasunsigned long millisecondsInHour; // Milissegundos em 1 horaint currentMode; // 1 =running - show time // 2 =time set // 3 =year set // 4 =day / month setfloat currentDate; // Datefloat juliano lastDate =0.0; // última data em que ShowDate foi chamadoint currentDay; int currentMonth; int currentYear; char * dayArray [] ={"Tue.", // Irá mostrar um aviso do compilador, mas funciona bem "Quarta", "Qui.", "Sex . "," Sat. "," Sun. "," Mon. "}; void setup () {// Configurar interrupção de tempo - millis () rola após 50 dias, então // estamos usando nosso próprio contador de milissegundos, que podemos redefinir no // final de cada dia TCCR0A =(1 < millisecondsInADay)) {// Próximo dia // Interrompe as interrupções durante o tempo de reinicialização noInterrupts (); currentTime - =milissegundosInADay; interrupções (); currentDate ++; } // No final de cada hora, ajuste o tempo decorrido para // a imprecisão no relógio do Arduino if (elapsed> =millisecondsInHour) {noInterrupts (); // Ajustar o tempo para o relógio do Arduino lento / rápido currentTime + =speedCorrection; // Reinicia para contar a próxima hora decorrida =0; interrupções (); } // Verifique se algum botão foi pressionado CheckButtons (); // Mostra a exibição com base na troca de modo atual (currentMode) {case SHOW_TIME:// Mostra a hora e data atuais ShowTime (currentTime); ShowDate (currentDate); pausa; case SET_TIME:// Mostra a tela para configuração da hora ShowTimeSet (currentTime); pausa; case SET_YEAR:// Mostra a tela para configuração do ano ShowYearSet (currentDate); pausa; case SET_DATE:// Mostra a tela para configuração do dia e mês ShowDDMMSet (currentDate); pausa; case SET_SPEED_ADJ:// Mostra a tela para ajuste da correção de velocidade ShowSpeedSet (); pausa; } Wait (150);} // Esta interrupção é chamada quando o tempo de comparação é atingido // portanto, será chamada uma vez por milissegundo com base na // configuração do registro OCR0A.ISR (TIMER0_COMPA_vect) {if (currentMode! =SET_TIME ) currentTime ++; decorrido ++;} float JulianDate (int iday, int imonth, int iyear) {// Calcula a data juliana (testado até o ano 20.000) unsigned long d =iday; m longo sem sinal =mês; sem sinal longo y =iano; se (m <3) {m =m + 12; y =y - 1; } t1 longo sem sinal =(153 * m - 457) / 5; t2 longo sem sinal =365 * y + (y / 4) - (y / 100) + (y / 400); return 1721118.5 + d + t1 + t2;} void GregorianDate (float jd, int &iday, int &imonth, int &iyear) {// Nota 2100 é o próximo ano bissexto ignorado - compensa os anos bissextos ignorados sem sinal longo f =jd + 68569.5; e longo sem sinal =(4,0 * f) / 146097; g longo sem sinal =f - (146097 * e + 3) / 4; h longo sem sinal =4000ul * (g + 1) / 1461001; t longo sem sinal =g - (1461 * h / 4) + 31; u longo sem sinal =(80ul * t) / 2447; longo sem sinal v =u / 11; iano =100 * (e - 49) + h + v; mês imediatamente =u + 2 - 12 * v; iday =t - 2447 * u / 80;} void SplitTime (sem sinal longo curr, sem sinal longo &ulHour, sem sinal longo &ulMin, sem sinal longo &ulSec) {// Calcule HH:MM:SS da contagem de milissegundos ulSec =curr / 1000; ulMin =ulSec / 60; ulHour =ulMin / 60; ulMin - =ulHour * 60; ulSec =ulSec - ulMin * 60 - ulHour * 3600;} SetTime longo sem sinal (ulHour longo sem sinal, ulMin longo sem sinal, ulSec longo sem sinal) {// Define o número de milissegundos desde a meia-noite até o retorno da hora atual (ulHour * 60 * 60 * 1000) + (ulMin * 60 * 1000) + (ulSec * 1000);} void Wait (unsigned long value) {// Criar nossa própria função dealy // Nós definimos nossa própria interrupção em TCCR0A // portanto millis () e delay () não funcionará mais unsigned long startTime =elapsed; while ((elapsed - startTime)  12) {iMonth =1; } // Define a data armazenada com base nas configurações atuais currentDate =JulianDate (iDay, iMonth, iYear); } if (digitalRead (DAY_BUTTON) ==LOW) {// Dia de avanço int iDay; int iMonth; int iYear; GregorianDate (currentDate, iDay, iMonth, iYear); iDay ++; if (iDay> 31) {iDay =1; } if (((iMonth ==4) || (iMonth ==6) || (iMonth ==9) || (iMonth ==11)) &&(iDay> 30)) {iDay =1; } if ((iMonth ==2) &&(iDay> 29)) {iDay =1; } if ((iMonth ==2) &&((iYear% 4)! =0) &&(iDay> 28)) {iDay =1; } // Definir data armazenada com base nas configurações atuais // Se posteriormente ajustar o mês para que o dia não seja válido // então o display avançará para a próxima data válida currentDate =JulianDate (iDay, iMonth, iYear); } pausa; case SET_SPEED_ADJ:// aumenta ou diminui a correção em 5 milissegundos se (digitalRead (UP_BUTTON) ==LOW) {speedCorrection + =5; } if (digitalRead (DOWN_BUTTON) ==LOW) {speedCorrection - =5; } pausa; }}} String FormatNumber (int value) {// Para adicionar um 0 inicial se necessário if (value <10) {return "0" + String (value); } else {return String (valor); }} void ShowTime (unsigned long value) {// Atualizar a exibição uma vez por segundo // ou quando rolar durante a meia-noite if ((value> lastTime + 1000) || (value  
Programa de cronômetro do Arduino Arduino
Este programa envia o número de milissegundos decorridos para a porta serial evert 2 segundos.
 // Paul Brace Fev 2021 // Para uso com o script de processamento correspondente // para comparar millis () daqui a millis () em // Processing usando o clockint do computador inByte =0; unsigned long firstReading =100000; // millis () na primeira leitura de sentvoid setup () {Serial.begin (9600); // Envie o byte hello para Processing sayHello ();} void loop () {// se um byte for recebido na porta serial // então leia e descarte-o e envie // o valor atual de millis () if (Serial. available ()> 0) {// obtém byte de entrada inByte =Serial.read (); // tempo de envio decorrido desde o processamento da primeira leitura Serial.print (millis () - firstReading); Serial.print ('E'); // repetir a cada 2 segundos delay (2000); }} void sayHello () {// Espere até que a porta serial esteja disponível // então envie hello byte para iniciar o handshake while (Serial.available () <=0) {Serial.print ('Z'); // Envia Z para processamento para dizer Olá, atraso (200); } firstReading =millis ();} 
Processando script de teste de cronômetro Processando
Este é o script para Processing que lerá os milissegundos enviados do Arduino e os comparará aos milissegundos decorridos no processamento.
 // Paul Brace Feb 2021 // Script para aceitar millis () do Arduino // e compará-lo com millis () interno para // avaliar a imprecisão do relógio do Arduino.// Assume que o relógio do computador é preciso // -ve =Arduino está lento, então insira um ajuste + ve no programa de relógio // + ve =Arduino está executando rápido, então insira como um ajuste -ve para diminuir o relógio para reduzir o processamento de importação. serial. *; Serial theSerialPort; // cria a porta serial objectint [] serialBytesArray =new int [15]; // array para armazenar bytesint bytesCount =0; // número atual de bytes recebidosboolean init =false; // falso até que o handshake seja concluído com o recebimento do caractere Zint fillColor =255; // definindo o preenchimento inicial colorlong mills =0; // última leitura recebida muito primeiro =0; // hora dos primeiros moinhos recebidos para que possamos calcular a diferença ao longo de uma hora agora; // número de milissegundos decorridos desde que os primeiros milissegundos receberamlong firstReading =100000; // millis () no processamento da primeira mensagem recebida de Arduinolong DiffPerHour =0; // a diferença após a primeira hora ter passado int inByte; // último byte readvoid setup () {// define alguns parâmetros de desenho e tela size (500, 500); fundo (70); noStroke (); // imprime a lista de todos os dispositivos seriais para que você saiba qual configurar para o Arduino // precisará executar o programa e editar se a porta correta não estiver configurada abaixo printArray (Serial.list ()); // instancia a string de comunicação serial thePortName =Serial.list () [1]; theSerialPort =new Serial (this, thePortName, 9600);} void draw () {// Exibir as configurações de tempo de fundo (70); fill (fillColor); textSize (25); texto (hora () + ":" + minuto () + ":" + segundo (), 50, 50); // os últimos milis lidos enviados pelo texto do Arduino ("Incoming elapsed:" + mills, 50, 100); // a corrente decorrida desde a primeira leitura em Processing text ("Local decorrido:" + (now - firstReading), 50, 150); // exibe o texto da diferença atual ("Diff:" + (mills - (now - firstReading)), 50, 200); // Verifique se 1 hora se passou e se a primeira hora armazena a diferença if (((now - firstReading)> =3600000) &&(DiffPerHour ==0)) {DiffPerHour =mills - (now - firstReading); } // Exibe a primeira diferença e a diferença após o texto da primeira hora ("Diferença após 1 hora:" + DiffPerHour, 50, 300);} void serialEvent (Serial myPort) {// lê um byte da porta serial inByte =myPort.read (); if (init ==false) {// se ainda não deu o handshaking o byte see if handshake if (inByte =='Z') {// se o byte lido é Z myPort.clear (); // limpa o buffer da porta serial init =true; // armazena o fato de que tivemos o primeiro hello myPort.write ('Z'); // diga ao Arduino para enviar mais if (first ==0) {first =millis (); }}} else {// se já havia o primeiro hello // Adicione o último byte da porta serial ao array if (inByte! =69) {// Não verifique o caractere de fim da mensagem E if (bytesCount <14) {serialBytesArray [bytesCount] =inByte; bytesCount ++; }} if (inByte ==69) {// Fim da mensagem // armazenar a hora local decorrida agora =millis (); // calcula millis () mills =0; para (int i =1; i <=bytesCount; i ++) {mills + =(serialBytesArray [i - 1] - 48) * pow (10, (bytesCount - i)); } // Digamos que estamos prontos para aceitar a próxima mensagem // se esta for a primeira leitura, defina a primeira diferença if (firstReading ==100000) {firstReading =now; } myPort.write ('Z'); // Reinicializa bytesCount:bytesCount =0; }}} 

Esquemas


Processo de manufatura

  1. Relógio de visão pov do Arduino
  2. Decodificador DTMF usando apenas Arduino
  3. Faça Monitor Ambilight usando Arduino
  4. Relógio de parede simples usando Adafruit 1/4 60 Ring Neopixel
  5. Simple Word Clock (Arduino)
  6. Relógio Arduino com horas de oração islâmica
  7. Relógio mestre
  8. Faça você mesmo voltímetro usando Arduino e Smartphone
  9. Monitor de freqüência cardíaca usando IoT
  10. WebServerBlink usando Arduino Uno WiFi