cionamento da sirene esta funcionando legal, mas eu quero ajustar a hora e data com os botões se necessário. os componentes são arduino, shield, rtc ds3231 e rele 5v. segue abaixo a programação. desde já agradeço a todos.
// Date and time functions using a DS1307 RTC connected via I2C and Wire lib #include #include "RTClib.h"
#include
// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(8,9,4,5,6,7);
RTC_DS1307 rtc;
int hora = 0;
int minuto = 0;
int segundo = 0;
void setup () {
Wire.begin();
rtc.begin();
// set up the LCD's number of columns and rows:
lcd.begin(16, 2);
Serial.begin(9600);
pinMode(3, LOW);
digitalWrite(3, OUTPUT);
pinMode(2, LOW);
digitalWrite(2, OUTPUT);
if (!rtc.isrunning()) {
lcd.print("RTC parado, vou ajustar com a hora da compilacao...");
rtc.adjust(DateTime(F(__DATE__), F( __TIME__)));
}
} void printnn(int n) { // imprime um numero com 2 digitos
// acrescenta zero `a esquerda se necessario
String digitos = String(n);
if (digitos.length() == 1) {
digitos = "0" + digitos;
}
lcd.print(digitos);
}
void loop () {
// Obtem a data e hora correntes e armazena em tstamp
DateTime tstamp = rtc.now();
hora = (tstamp.hour());
minuto = (tstamp.minute());
segundo = (tstamp.second());
// Mostra no monitor serial a data e hora correntes
lcd.setCursor(4, 0);
lcd.print("SINALTEC");
//printnn(tstamp.day());
//lcd.print('/');
//printnn(tstamp.month());
//lcd.print('/');
//lcd.print(tstamp.year(), DEC);
lcd.setCursor(3, 1);
lcd.print (' ');
printnn(tstamp.hour());
lcd.print(':');
printnn(tstamp.minute());
lcd.print(':');
printnn(tstamp.second());
delay(1000);
lcd.clear();
//lcd.println();
Serial.print (hora);
Serial.print (":");
Serial.print (minuto);
Serial.print (":");
Serial.println (segundo);
// Periodo da manha
if (hora == 7 && minuto == 35 && segundo == 0) {
alarme();
}
if (hora == 9 && minuto == 35 && segundo == 0) {
alarme();
}
if (hora == 9 && minuto == 50 && segundo == 0) {
alarme();
}
if (hora == 11 && minuto == 35 && segundo == 0) {
alarme();
}
//periodo da tarde
if (hora == 13 && minuto == 20 && segundo == 0) {
alarme();
}
if (hora == 15 && minuto == 35 && segundo == 0) {
alarme();
}
if (hora == 15 && minuto == 50 && segundo == 0) {
alarme();
}
if (hora == 17 && minuto == 20 && segundo == 0) {
alarme();
}
//periodo da noite
if (hora == 19 && minuto == 00 && segundo == 0) {
alarme();
}
if (hora == 20 && minuto == 40 && segundo == 0) {
alarme();
}
if (hora == 21 && minuto == 34 && segundo == 0) {
alarme();
}
if (hora == 01 && minuto == 52 && segundo == 0) {
alarme();
}
} void alarme() { digitalWrite(3, HIGH);
digitalWrite(2, HIGH);
lcd.setCursor(5, 0);
lcd.print ("FEIRA");
lcd.setCursor(0, 1);
lcd.print ("TECNOLOGICA 2017");
delay(5000);
digitalWrite(3, LOW);
digitalWrite(2, LOW);
lcd.clear();
}…
s polo sul),
O sol,( na realidade a terra) tem 2 inclinações máximas em relação ao sol.
O solstício de Inverno e solstício de verão, Tomando por base o equador, ( 0 graus), no solstício de verão,
a terra está inclinada a 23,5 graus, e no solstício de inverno, a -23,5 graus.
Estes verão e inverno que me referi, são do hemisfério norte.
Qdo está perpendicular ao equador nos temos os equinócios (De primavera e de outono).
http://www.infoescola.com/geografia/solsticio-e-equinocio/ este link ajuda um pouco a entender.
Com estas informações, e sabendo que a terra leva 365 dias, 6 horas e alguns segundos para dar uma volta
em redor do sol, e que este ciclo de inclinação é sincronizado com esta volta, podemos calcular a posição
da terra em relação ao sol, os horários do nascer e por do sol e ainda a duração e inclinação da da luz solar sobre nós.
Há alguns meses desenvolvi para um amigo,(não do LdG), umsistema para usar placas fotovoltaicas, mantendo-as
sempre perpendicular ao sol a cada segundo e escrevi um algorítimo para este cálculo.
Depois escrevi um code para o arduino usar 2 motores de passo controlando o movimento e acompanhado
o movimento do sol. Este calculo envolve a latitude, o horário, e o dia do ano.
No final do dia solar, os motores reposicionam as placas para o inicio do próximo dia e ficam a espera do amanhecer.
Caso falte energia durante o dia, ao retornar a energia, as placas são reposicionadas no incio, e em seguida
avançam até a posição que deveriam estar no exato momento que a energia foi restabelecida.
É importante o uso de RTC de boa precisão. Eu usei um DS3231.
Como aprendizado, recomendo você desenvolver o restante do code, mas caso não tenha
conhecimentos ou desenvoltura em C posso disponibilizar o code completo.
Rui
Obs. Não gosto de postar code na área de texto, mas como era bem pequeno e ajuda a entender o funcionamento
resolvi postar.
//----------------------------------------------------------------------------- void CalculosGeo () { d = tan(23.45*sin(((float)360/365*(284+MeuDia)*Rad))*Rad); // Calcula declinacao da terra d = -(d * TgF); // Declinacao da terra = declinação * tang das coord Nht = ((float)2/15)*(acos(d))/PI*180; // Calcula horas total de sol por dias (float) Nh = Nht; // Horas de sol por dia (long) Nm = (Nht-Nh)*60; // Minutos de sol por dia (long) Ns = (Nht-Nh)*3600-Nm*60; // Segundos de sol por dia (long)// Serial.print("Tempo de sol: "); // Serial imprime "Tempo de sol: "// Imprime(); // Serial imprime o tempo total de sol do dia TTotal = (Nh*3600+ Nm*60+ Ns); // Metade do tempo total de seg de sol por dia Inicio = 43200 - (TTotal/2); // Meio dia menos metade do tempo de sol Fim = 43200 + (TTotal/2); // Meio dia mais metade do tempo de sol Move = 648000/float(TTotal); // Segs de graus de deslocamento por seg de hora }…
Adicionado por mineirin RV ao 9:31 em 17 setembro 2016
os correios, mais deixa pra lá.
Quando finalizamos este projeto com a preciosa ajuda do Mestre Minerim (Rui Viana), resolvi testar os serviços da EasyEDA encomendando as placas para este projeto.
Claro que surgiram algumas dificuldades.
Em primeiro lugar foi que, se não tivesse um conector adaptador TQFP32 x DIP 28 seria muito dificil prosseguir com os trabalhos.
Quando tentei gravar pela primeira vez meu Atmega328p-au (smd), veio o primeiro desafio. Não sabia que o mesmo vinha virgem, ou seja, tinha de gravar o bootloader. Fiz até uma plaquinha adicional para "encaixar" do adaptador ao arduino.
Pronto, resolvido esta parte.
Gravei também o sketch no atmega e parti para a segunda etapa que foi a montagem.
Antes porém, separei os componentes e percebi dois "erros" que cometi ao fazer o projeto das placas..
O primeiro foi de não observar o tamanho da chave tactil, e que no meu mercado aqui em Campinas eu não encontraria tais chaves para se aplicar na minha placa. Resolvi comprando as chaves um pouquinho maior, tal anciedade em ver o projeto funcionando.
O outro erro também se deu ao fazer o dimensionamento do cristal. Este que será aplicado a placa é do tamanho 5032, deveria ter escolhido um cristal normal (pth) ou mesmo um do tamanho HC49-S.
Este componente é um pouco chato para se comprar no mercado interno (não se acha fácil e com preço acessível) então terei de optar por Ebay ou Aliexpress da vida, mais para não esperar por mais 5 meses, farei uma arranjo técnico até a chegada do mesmo.
Enfim veio a parte da soldagem:
No início foi meio complicado para quem nunca trabalhou com estes componentes, mais depois fui pegando o jeito e creio que está ficando show. Começei com os resistores/capacitores, depois com o DS3231 e por fim o mais complicado que foi o atmega328. Com auxílio do microscópio ficou mais fácil.
Mais uma vez meu agradecimento ao RV pela grandiosa colaboração do sketch.
Bom fim de semana a todos,
J.Antonio
…
ncionou bem (programa abaixo), depois copiei um programa para realizar as leituras do sensor DS18B20 e consegui obter as leituras, ótimo até ai tudo bem, então pensei em achar um programa de menu para usar o KeyPad Shield, consegui achar um bem bacana e o traduzi pro português, (conforme abaixo), e o mesmo esta funcionando bem, navego nas opções e quando pressiono o botão select ele chama a opção selecionada, até ai tudo bem, o que eu gostaria era saber como eu faço para por neste menu as opções para fazer aparecer no LCD os dados de de data e hora quando por exemplo selecionar a opção 1 do menu, e depois aparecer a temperatura do DS18B20 quando seleciono a opção 2. Talvez esta seja uma solução muito básica, porém pra mim que sou muito leigo no assunto parece um dragão de sete cabeças kkkk. Agradeço antecipadamente pela atenção e ajuda.
//----------------------------------------------------------------------------------------------------------------------------------------------
//PROGRAMA PARA APARECER OS DADOS DO RTC NO DISPLAY
//obs ja tem alguns codigos do DS18B20
#include <Wire.h>#include "ds3231.h"#include "rtc_ds3231.h"#include <LiquidCrystal.h>#include <OneWire.h>#include <DallasTemperature.h>
#define BUFF_MAX 128#define ONE_WIRE_BUS 2 //variavel do pino que esta plugado o Sensor
OneWire oneWire(ONE_WIRE_BUS); //Instacia o Objeto oneWire e Seta o pino do Sensor para iniciar as leiturasDallasTemperature sensor(&oneWire);//Repassa as referencias do oneWire para o Sensor Dallas (DS18B20)uint8_t time[8];char recv[BUFF_MAX];unsigned int recv_size = 0;unsigned long prev, interval = 1000;char minuto = 0;char hora = 0;
LiquidCrystal lcd(8, 9, 4, 5, 6, 7); void setup() { //-------------------------------------------------------------------------------------//INICIO BLOCO TESTE BOTOES DISPLAY //lcd.begin(16, 2); //lcd.setCursor(0,0); //lcd.print("Arduino e Cia"); //lcd.setCursor(0,1); //lcd.print("Tecla :"); //FIM BLOCO TESTE BOTOES DISLPLAY//-------------------------------------------------------------------------------------
pinMode(7, OUTPUT); //PINO DO RELE Serial.begin(9600); Serial.println("Sensor de temperatura Dallas DS18b20"); sensor.begin(); Wire.begin(); DS3231_init(DS3231_INTCN); memset(recv, 0, BUFF_MAX); //Serial.println("GET time"); lcd.begin(16, 2); lcd.clear(); // *************************************** // * PARA AJUSTAR RELOGIO *// * Serial.println("Setting time"); *// * parse_cmd("T301218513112015",16); *// *************************************** } void loop() { //SENSOR DE TEMPERATURA LIQUIDOS DS18B20 sensor.requestTemperatures(); //Envia o comando para obter temperaturas float leitura=sensor.getTempCByIndex(0); // A temperatura em Celsius para o dispositivo 1 no índice 0 (é possivel ligar varios sensores usando a mesma porta do arduino) Serial.println(leitura); // INICIO FUNÇÃO DO RELOGIO char in; char tempF[6]; float temperature; char buff[BUFF_MAX]; unsigned long now = millis(); struct ts t; // show time once in a while if ((now - prev > interval) && (Serial.available() <= 0)) { DS3231_get(&t); //Get time parse_cmd("C",1); temperature = DS3231_get_treg(); //Get temperature dtostrf(temperature, 5, 1, tempF);
lcd.clear(); lcd.setCursor(0,0); lcd.print(t.mday); printMonth(t.mon); lcd.print(t.year); lcd.setCursor(0,1); //Go to second line of the LCD Screen lcd.print(t.hour); lcd.print(":"); if(t.min<10) { lcd.print("0"); } lcd.print(t.min); lcd.print(":"); if(t.sec<10) { lcd.print("0"); } lcd.print(t.sec); lcd.print(' '); lcd.print(tempF); lcd.print((char)223); lcd.print("C "); prev = now; }
if (Serial.available() > 0) { in = Serial.read();
if ((in == 10 || in == 13) && (recv_size > 0)) { parse_cmd(recv, recv_size); recv_size = 0; recv[0] = 0; } else if (in < 48 || in > 122) {; // ignore ~[0-9A-Za-z] } else if (recv_size > BUFF_MAX - 2) { // drop lines that are too long // drop recv_size = 0; recv[0] = 0; } else if (recv_size < BUFF_MAX - 2) { recv[recv_size] = in; recv[recv_size + 1] = 0; recv_size += 1; }
} // SELEÇÃO DA CADA BOTÃO DO DISLPLAY int botao; botao = analogRead (0); //Leitura do valor da porta analógica A0 //Serial.println(botao); lcd.setCursor(8,1); if (botao < 100) { lcd.print ("Direita "); } else if (botao < 200) { lcd.print ("Cima "); } else if (botao < 400){ lcd.print ("Baixo "); } else if (botao < 600){ lcd.print ("Esquerda"); } else if (botao < 800){ lcd.print ("Select "); } }//FINAL DE SELEÇÃO DE BOTOES//------------------------------------------------------------------------------
void parse_cmd(char *cmd, int cmdsize){ uint8_t i; uint8_t reg_val; char buff[BUFF_MAX]; struct ts t;
if (cmd[0] == 84 && cmdsize == 16) { t.sec = inp2toi(cmd, 1); t.min = inp2toi(cmd, 3); t.hour = inp2toi(cmd, 5); t.wday = inp2toi(cmd, 7); t.mday = inp2toi(cmd, 8); t.mon = inp2toi(cmd, 10); t.year = inp2toi(cmd, 12) * 100 + inp2toi(cmd, 14); DS3231_set(t); Serial.println("OK"); } else if (cmd[0] == 49 && cmdsize == 1) { // "1" get alarm 1 DS3231_get_a1(&buff[0], 59); Serial.println(buff); } else if (cmd[0] == 50 && cmdsize == 1) { // "2" get alarm 1 DS3231_get_a2(&buff[0], 59); Serial.println(buff); } else if (cmd[0] == 51 && cmdsize == 1) { // "3" get aging register Serial.print("aging reg is "); Serial.println(DS3231_get_aging(), DEC); } else if (cmd[0] == 65 && cmdsize == 9) { // "A" set alarm 1 DS3231_set_creg(DS3231_INTCN | DS3231_A1IE); //ASSMMHHDD for (i = 0; i < 4; i++) { time[i] = (cmd[2 * i + 1] - 48) * 10 + cmd[2 * i + 2] - 48; // ss, mm, hh, dd } boolean flags[5] = { 0, 0, 0, 0, 0 }; DS3231_set_a1(time[0], time[1], time[2], time[3], flags); DS3231_get_a1(&buff[0], 59); Serial.println(buff); } else if (cmd[0] == 66 && cmdsize == 7) { // "B" Set Alarm 2 DS3231_set_creg(DS3231_INTCN | DS3231_A2IE); //BMMHHDD for (i = 0; i < 4; i++) { time[i] = (cmd[2 * i + 1] - 48) * 10 + cmd[2 * i + 2] - 48; // mm, hh, dd } boolean flags[5] = { 0, 0, 0, 0 }; DS3231_set_a2(time[0], time[1], time[2], flags); DS3231_get_a2(&buff[0], 59); Serial.println(buff); } else if (cmd[0] == 67 && cmdsize == 1) { // "C" - get temperature register // Serial.print("temperature reg is "); //Serial.println(DS3231_get_treg(), DEC); } else if (cmd[0] == 68 && cmdsize == 1) { // "D" - reset status register alarm flags reg_val = DS3231_get_sreg(); reg_val &= B11111100; DS3231_set_sreg(reg_val); } else if (cmd[0] == 70 && cmdsize == 1) { // "F" - custom fct reg_val = DS3231_get_addr(0x5); Serial.print("orig "); Serial.print(reg_val,DEC); Serial.print("month is "); Serial.println(bcdtodec(reg_val & 0x1F),DEC); } else if (cmd[0] == 71 && cmdsize == 1) { // "G" - set aging status register DS3231_set_aging(0); } else if (cmd[0] == 83 && cmdsize == 1) { // "S" - get status register Serial.print("status reg is "); Serial.println(DS3231_get_sreg(), DEC); } else { Serial.print("unknown command prefix "); Serial.println(cmd[0]); Serial.println(cmd[0], DEC); } }
void printMonth(int month){
switch(month) { case 1: lcd.print(" Janeiro ");break; case 2: lcd.print(" Fevereiro ");break; case 3: lcd.print(" Março ");break; case 4: lcd.print(" Abril ");break; case 5: lcd.print(" Maio ");break; case 6: lcd.print(" Junho ");break; case 7: lcd.print(" Julho ");break; case 8: lcd.print(" Agosto ");break; case 9: lcd.print(" Setembro ");break; case 10: lcd.print(" Outubro ");break; case 11: lcd.print(" Novembro ");break; case 12: lcd.print(" Dezembro ");break; default: lcd.print(" Error ");break; } } //FINAL FUNÇÃO RELOGIO//---------------------------------------------------------------------------
//PROGRAMA DE MENUS PARA LCD KEYPAD SHIELD
#include <LiquidCrystal.h>// Pinos para LCD Shield Keypad - Não serve para lcd comumLiquidCrystal lcd(8, 9, 4, 5, 6, 7);//Estados do menusint atualMenuItem = 0;int lastState = 0;void setup() {//Iniciando o LCD 16x2 do Display Keypad.lcd.begin(16, 2);//Mostar o Titulo do Menu.clearPrintTitle();}void loop() {//Chma o menu principalmainMenu();}void mainMenu() {//Estado = 0 cada ciclo de loop.int state = 0;//Atualiza o botão pressionado.int x = analogRead (0);//Define a linha e a coluna.lcd.setCursor(0,0);//Verifica os valores analogicos da porta A0 do LCD Keypad Shieldif (x < 100) {//Direita} else if (x < 200) {//Cimastate = 1;} else if (x < 400){//Baixostate = 2;} else if (x < 600){//Esquerda} else if (x < 800){//Selecionarstate = 3;}//Se os limites do MENU for ultrapassados o reiniciamosif (atualMenuItem < 0 || atualMenuItem > 4) {atualMenuItem = 0; }//Se mudarmos o index o redesenhamos.if (state != lastState) {if (state == 1) {//Se pra cimaatualMenuItem = atualMenuItem - 1; displayMenu(atualMenuItem);} else if (state == 2) {//Se pra baixoatualMenuItem = atualMenuItem + 1; displayMenu(atualMenuItem);} else if (state == 3) {//Se selecionadoselectMenu(atualMenuItem); }//Salva o estado anterior para compara-lo.lastState = state;} //Pequeno delaydelay(5);}//Opções do menu.void displayMenu(int x) {switch (x) {case 1:clearPrintTitle();lcd.print ("-> Menu Opcao 1");break;case 2:clearPrintTitle();lcd.print ("-> Menu Opcao 2");break;case 3:clearPrintTitle();lcd.print ("-> Menu Opcao 3");break;case 4:clearPrintTitle();lcd.print ("-> Menu Opcao 4");break;}}//Bloco para o cabeçalho do MENU.void clearPrintTitle() {lcd.clear();lcd.setCursor(0,0);lcd.print(" MEU-MENU ");lcd.setCursor(0,1); }//Bloco que mostra a seleção na telavoid selectMenu(int x) {switch (x) {case 1:clearPrintTitle();lcd.print ("Op 1 Selecionada");//Chama a função da Opção 1break;case 2:clearPrintTitle();lcd.print ("Op 2 Selecionada");//Chama a função da Opção 2break;case 3:clearPrintTitle();lcd.print ("Op 3 Selecionada");//Chama a função da Opção 3break;case 4:clearPrintTitle();lcd.print ("Op 4 Selecionada");//CChama a função da Opção 4break;}}…
GPRS e postagem das informações em um servidor WEB. Já está desenvolvida grande parte do projeto, atualmente a estação é composta por um Arduino Mega, um sensor pluviométrico, um módulo RTC DS3231(Possui sensor de temperatura interno), um módulo de cartão SD de forma a funcionar como um back-up dos dados e um módulo GSM/GPRS SIM800L para realizar a comunicação com o servidor WEB através de comandos AT. O objetivo do meu trabalho é realizar o monitoramento continuo da quantidade de precipitação(mm) e temperatura(Cº) do local em que será instalada a estação e exibi-los num servidor WEB anteriormente criado. Todas as informações lidas(Precipitação e temperatura) são armazenadas no cartão SD, o meu problema atualmente está em ler os dados armazenados no cartão SD (armazenados em um arquivo .CSV separados por ",") e enviá-los de forma controlada e correta para o servidor. Preciso realizar a leitura dos dados armazenados de 1 dia e enviá-los. Não estou conseguindo realizar a leitura especifica(de apenas 1 dia) dos dados, apenas consigo realizar a leitura de todos os dados armazenados no cartão SD. Alguém teria alguma sugestão? Ou alguem ja passou por este mesmo caso? Desde já muitíssimo obrigado!
Segue abaixo o código que estou utilizando para realizar a leitura dos arquivos dentro do cartão SD:
O código abaixo realiza a leitura de todos os arquivos contidos no cartão SD, no caso, eu quero realizar a leitura controlada e específica dos dados, por exemplo, a leitura de apenas 1 dia de dados armazenados e não a leitura desordenada de todos os dados.
#include <SoftwareSerial.h>#include <SD.h>#include <SPI.h>
/* CSV File Reading */File file;
bool readLine(File &f, char* line, size_t maxLen) { for (size_t n = 0; n < maxLen; n++) { int c = f.read(); if ( c < 0 && n == 0) return false; // EOF if (c < 0 || c == '\n') { line[n] = 0; return true; } line[n] = c; } return false; // line too long}
bool readVals(float* v1, float* v2, String* loc, String* loc2) { char line[200], *ptr, *str; if (!readLine(file, line, sizeof(line))) { return false; // EOF or too long } *v1 = strtod(line, &ptr); if (ptr == line) return false; // bad number if equal while (*ptr) { if (*ptr++ == ',') break; } *v2 = strtod(ptr, &str); while (*ptr) { if (*ptr++ == ',') break; }
String a = strtok_r(ptr, ",", &str); String first(str); *loc = first; String let(a); *loc2 = let;
return str != ptr; // true if number found}/* Close CSV File Reading */
void setup(){ pinMode(53, OUTPUT); //SD Card Reader Setup Serial.begin(115200); if (!SD.begin(53)) { Serial.println("begin error"); return; } file = SD.open("LOGTESTE.csv", FILE_READ); if (!file) { Serial.println("open error"); return; }}
void loop() // run over and over{ float x, y; String string1, string2, loc, loc2;
while (readVals(&x, &y, &loc, &loc2)) {
string1 = "sensor=" + String(x); // Dados do pluviômetro string2 = "&sensor1=" + String(y); // Dados do sensor de temperatura Serial.print(string1 + string2); // Concatenando os dados em uma única string para enviá-los via método POST para o servidor WEB Serial.println("," + loc + "," + loc2); // Concatenando dados obtidos pelo RTC. loc é a data e loc2 é o horário da leitura delay(100); }}
Segue em anexo o arquivo que estou utilizando como teste para as leituras.…
(LED) após um período de tempo quando o acionamento é feito pelo Botão ou pelo Sensor PIR, então foi necessário acrescentar mais um estado, com o que a Máquina passou a ter 3 estados.
Nesta Parte 1, estarei publicando a implementação do Sistema de Controle da Lâmpada do Aquário, usando Arduino UNO. Mas a implementação poderá ser estendida a qualquer outra Placa disponível que utilize a Plataforma Arduino (Nano, ESP32, ESP8266, Due, etc).
Nos posts seguintes a este, a implementação será descrita na seguinte ordem, onde cada item será um "post", iniciando pela Parte 2:
2) descrição dos elementos e estruturas de dados usados na implementação, e das implicações resultantes de algumas destas definições.
3) descrição dos Estados e Eventos necessários para o Sistema funcionar, e das implicações resultantes.
4) descrição da técnica básica de implementação de uma Máquina de Estados (na Plataforma Arduino e de forma genérica), usando como referência este exemplo do Controle do Aquário e/ou outros exemplos simples.
5) apresentação de possibilidades para a implementação, usando técnicas mais "avançadas" ou sofisticadas.
Assim a Parte 1, efetivamente se inicia a seguir:
O Diagrama de Estados da Máquina de Estados que controla este Sistema, pode ser visto na figura a seguir:
(clique na figura para "zoom")
O Diagrama em si é simples e praticamente dispensaria explicações. Mas eu entendo que para quem for principiante poderá ter dificuldades em entender a dinâmica do funcionamento, e por isso mesmo (como dito anteriormente), em outro post irei descrever a técnica e a metodologia por trás da implementação dessa Máquina de Estados.
A implementação foi feita também em Simulação no Proteus, o que dá grande flexibilidade para testar as diversas condições e possibilidades de configuração do código. O Hardware para a Simulação no Proteus, é mostrado na figura a seguir:
(clique na figura para "zoom")
Observar que embora na simulação tenha sido utilizado um Sensor PIR de autoria do "TEP" ("The Engineering Projects"), isto é quase um "enfeite", uma vez que na simulação o PIR poderia ser simplesmente substituído por um Botão (ou então um Monoestável e um Botão).
Observar também, que após a inicialização, algumas informações são exibidas no Terminal do Arduino, a saber:
- o "total de comandos": isto está descrito em mais detalhes logo adiante no texto.
- o "tempo de apagamento automático", que é o tempo que a Lâmpada apagará automaticamente após ter sido ligada pelo Botão ou pelo Sensor PIR. Observar que no código da simulação, eu deixei apenas 20 segundos, para ficar mais dinâmico o teste (ou seja, não ter que esperar muito pra ver se o apagamento automático está funcionando). Mas no código aqui publicado, este tempo está setado em 60 segundos, mas deve ser alterado conforme se deseja (ele está definido logo no início do código).
- a Data/Hora atual lida do RTC DS3231 (incluindo o Dia da Semana, informado em Português). Sobre este ponto, no código da simulação foi setado através do Botão apertado logo que o Arduino é iniciado, um dia e horário que permitisse verificar rapidamente o funcionamento para os Dias e Horários de acendimento e apagamento programados no Sistema. E foi verificado o correto funcionamento (na simulação, programei para ligar às 17:01 e para desligar às 17:03 do domingo).
Sobre o "total de comandos" vou esclarecer.
O método utilizado para se "programar" os Dias e Horários que se quer ligar/desligar a Lâmpada, é utilizando um "Vetor de Comandos", ou seja, uma "Lista de Comandos". Estes Comandos podem estar em qualquer ordem no Vetor (a "Lista"), uma vez que está descrito no próprio Comando qual o Dia e Horário que o comando deve ser executado. Vejamos um exemplo na figura a seguir, onde são mostrados 4 Comandos na "Lista":
(clique na figura para "zoom")
Observe que o primeiro elemento de um Comando, é o Dia da Semana (segunda, sexta, domingo, etc), seguido pelo horário (Hora e Minuto), e por último se o Comando é para ligar ou desligar a Lâmpada. Assim, já adiantando parte de como a coisa funciona (pois isto será efetivamente descrito no próximo post): o tal "Vetor de Comandos" é uma "Lista de Comandos", a qual será "varrida" pelo código para execução de cada um dos Comandos, assim ligando ou desligando a Lâmpada no Dia e Horário descrito em cada Comando (e claro, o Dia/Horário atual é fornecido pelo RTC DS3231 do Sistema).
Notar que embora os Comandos possam estar na Lista em qualquer ordem, é aconselhável por motivos óbvios de organização, que se procure colocar sequencialmente na Lista um Comando para ligar seguido de um Comando para desligar. Ou seja, assim fica fácil observar olhando a sequência na Lista (ou Vetor), como irá funcionar toda a programação feita para o Controle da Lâmpada.
Pode-se ter quaisquer quantidades de Comandos na Lista. Cada Comando ocupa 4 bytes na Memória RAM do Arduino, e uma vez que ainda resta de RAM bem mais de 1000 bytes, então isto permitiria se ter bem além de 250 comandos programados para execução. Ou seja, tranquilo em relação à uma eventual necessidade de programação de uma grande quantidade de comandos.
No código que está anexado nesta publicação, eu deixei apenas 6 comandos, e por este motivo este é o valor mostrado no Terminal do Arduino.
Acrescentar Comandos é simples. A única coisa necessária é colocá-los na Lista, a qual está logo no início do código (lá a Lista é chamada de "Vetor_Comandos"). Mais nada precisa ser feito.
Para ilustrar essa facilidade, é mostrada na figura a seguir, como a "Lista" com os 6 comandos está definida no código que estou disponibilizando neste post:
(clique na figura para "zoom")
Observar que os nomes dos dias da semana podem ser escritos em Português, pois estes nomes também foram definidos no início do código (e são compatíveis com os nomes em inglês definidos na Biblioteca DS3231).
O código do Arduino, os Arquivos para Simulação, e o Diagrama de Estados (Visio e PDF), estão neste link:
Lampada_Aquario_01.zip
O Sistema foi testado, mas caso seja detectada alguma anomalia no funcionamento, basta sinalizar aqui, que irei considerar logo que possível.
Não se deixe enganar achando que existe alguma complexidade. Não existe. Ocorre que o tratamento "anti-bouncing" para o Botão (e algo semelhante para o PIR), exigiu rotinas um pouco maiores. Mas isto é apenas devido ao "anti-bouncing", e nada mais. E também foram necessárias algumas rotinas a mais para o RTC, com funções bem simples.
Sobre o funcionamento e porque o Sistema foi implementado dessa forma (usando os "Comandos"), será abordado nos próximos posts.
Abrçs,
Elcids…
Adicionado por Elcids Chagas ao 21:33 em 14 setembro 2019
) ao usar o timer do arduino como base de tempo para projetos ,medidas de pulsos, sincronismos, etc
Pessoal. a ideia aqui é discutir o oscilador e o timer do arduino, e agradeço se encontrem erros
ou equívocos da minha parte, que me corrijam e melhorem a informação.
O oscilador dos arduinos (Uno e mega) originais é de 16 MHz.
Eles são montados com cristais de quartzo. 16.000.000 ciclos por segundos.
Variação da frequência destes cristais:
F-Deviation = (PPM/1.000.000)*F-Ideal.
Os cristais comerciais são encontrados na faixa de 20 a 50 PPM.
Ex: F-Deviation = (50/1000000) * 16000000 = 800 Hz. um erro de 0,005%
Referencia:
http://www.jameco.com/z/CY16LP-16-MHz-Crystal-50ppm-HC49-S_137891.html?CID=GOOG&gclid=CIjXv8uRmc8CFYpkhgodlQ8EsA
Isto significa uma variação de 4 segundos (pra mais ou pra menos) ao final de 24 horas
24 horas = 24*60*60 segundos = 86400 segundos 86400 * 0,005% = 4,32 segundos de erro por dia.
Mas como é um cristal oscilador, ele tem uma característica de estabilidade, ou seja, este desvio é constante na razão da estabilidade da temperatura.
A variação de frequência é de aproximadamente 0,7 Hz, por grau centigrado, ou 0,000004375%, o que representa uma variação de 0,00378 segundos ao dia.
Os RTCs de mais alta precisão, como por exemplo o DS3231, utlizam cristais montados internamente no CI, ajustados para a frequencia central, e com PPM baixos na ordem de 2 PPM
e ainda usam o recurso de estabilidade térmica.
• Accuracy ±2ppm from 0°C to +40°C; 0,0002% Variação 0,017 segundos dia.
• with an integrated temperaturecompensated crystal oscillator (TCXO) .....
Referencia:
https://datasheets.maximintegrated.com/en/ds/DS3231.pdf
Com base nestas informações, podemos identificar o desvio de frequência do nosso arduino e usar este valor de desvio para corrigir erros na contagem do tempo.
A estabilidade da temperatura é um pouco mais complicada de fazer, mas ela é muito pequena e dependerá da precisão que alvejamos.
Você pode usar um sensor de temperatura encostado no cristal, e medira atemperatura nele, e depois compensar a contagem com o desvio calculado a partir desta temperatura.
Agora vamos falar da biblioteca TimerOne.
Esta é uma biblioteca que permite voce usar um recurso do arduino UNO/MEGA e outros, é o TIMER1 (não serve para o DUE).
Ela permite que você defina uma contagem de tempo usando o registrador TIMER1 do arduino.
Este TMR1 é programado pela biblioteca para receber o incrementos vindos do oscilador do arduino, ou seja um pulso a cada 62,5 NanoSegundos, através de prescaler definido no momento da inicialização do timer1.
Prescaler é um circuito que divide a contagem dos pulsos antes de aplica-los no timer.
Por exemplo, se o prescaler está definido como 16, a cada 16 pulsos na entrada, do circuito, um pulso será incrementado no registrador do timer.
Se estiver definido como 1, todos pulsos na entrada do circuito incrementarão o registrador.
O Timer1 tem os seguintes prescalers 1, 8, 64, 256 e 1024. Reg TCCR1B do Atmega.
A definição da biblioteca para este prescaler é feita no momento em que se inicializa o timer1.
Este timer, nesta configuração, é incrementado, independente do processador do arduino, ele roda "stand alone" bastando os pulsos do oscilador.
Esta biblioteca permite também que você defina um valor, que ao ser atingido pelo timer1, um interrupt seja gerado, ou seja interrompe-se tudo que o processador está executando e executa uma função definida pelo usuário.
O Timer 1 é zerado e reinicia a contagem novamente independente do processador, ao gerar o interrupt.
Desta forma, você pode por exemplo gerar um interrupt a cada 100ms ou a cada seg, e acumular este tempo gerando uma base de tempo, como fosse um relógio.
Com esta linha: Timer1.initialize(100000); você diz para o timer gerar um interrupt a cada 100ms, ou com esta: Timer1.initialize(1000000); um interrupt a cada segundo.
Se você conseguir identificar o desvio do cristal do seu arduino, pode alterar estes valores para que então, apesar de não ser exato o valor do seu cristal, você consiga gerar uma contagem de um segundo com grande precisão.
Ex: meu arduino aumenta 4 segundos por dia, então ele gerou 4.000.000 usegundos a mais.
4.000.000 /86400 = 46 u segundos por segundo.
Então defina a linha assim: Timer1.initialize(1000046); e deverá ter um "relógio bem preciso".
Rui…
Adicionado por mineirin RV ao 18:16 em 18 setembro 2016
status.
vlw pelas dicas
#include <DS3231.h>#include <Wire.h>[#include <SPI.h>#include <Ethernet.h>int buzzer = 13;
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };byte ip[] = { 192, 168, 1, 235 };EthernetServer server(80);String readString;int Pin = 13;
DS3231 Clock;bool Century=false;bool h12;bool PM;byte ADay, AHour, AMinute, ASecond, ABits;bool ADy, A12h, Apm;byte year, month, date, DoW, hour, minute, second;
void setup() { pinMode(Pin, OUTPUT); Ethernet.begin(mac, ip); server.begin(); pinMode (buzzer, OUTPUT);// Start the I2C interfaceWire.begin();// VERY IMPORTANT COMMENTS BELOW!!!// If you want to set the time, change these numbers to the date and time you want to set to, and then upload it to the arduino.// once you have finished setting the time, comment out the following clock.set functions and then reupload it to the board. Otherwise your clock will reset every time you open the serial monitor.// Clock.setSecond(05);//Set the second// Clock.setMinute(37);//Set the minute// Clock.setHour(10); //Set the hour// Clock.setDoW(7); //Set the day of the week// Clock.setDate(18); //Set the date of the month// Clock.setMonth(10); //Set the month of the year// Clock.setYear(15); //Set the year (Last two digits of the year)// Start the serial interfaceSerial.begin(9600);}void ReadDS3231(){ int second,minute,hour,date,month,year,temperature; second=Clock.getSecond(); minute=Clock.getMinute(); hour=Clock.getHour(h12, PM); date=Clock.getDate(); month=Clock.getMonth(Century); year=Clock.getYear(); // Definicao do alarme.if (hour == 10 && minute ==39 && second == 0)//Define a hora de tocar o alarme por 1o segundos{digitalWrite (buzzer, HIGH);// Eleva tensao pino 13 para 5Vdelay (10000);// Atrasa 50ms}{if (hour == 10 && minute == 56 && second == 05)//Define a hora de tocar o alarme por 10 segundos
digitalWrite (buzzer, LOW); //Abaixa tensao pino 13 para 0V}
temperature=Clock.getTemperature();
Serial.print("20"); Serial.print(year,DEC); Serial.print('-'); Serial.print(month,DEC); Serial.print('-'); Serial.print(date,DEC); Serial.print(' '); Serial.print(hour,DEC); Serial.print(':'); Serial.print(minute,DEC); Serial.print(':'); Serial.print(second,DEC); Serial.print('\n'); Serial.print("Temperature="); Serial.print(temperature); Serial.print('\n');}void loop() {ReadDS3231();delay(1000);// send what's going on to the serial monitor.// Start with the year/* Serial.print("2");if (Century) { // Won't need this for 89 years.Serial.print("1");} else {Serial.print("0");}Serial.print(Clock.getYear(), DEC);Serial.print('-');// then the monthSerial.print(Clock.getMonth(Century), DEC);Serial.print('-');// then the dateSerial.print(Clock.getDate(), DEC);Serial.print(' ');*/// and the day of the week/*Serial.print(Clock.getDoW(), DEC);Serial.print(' ');*/// Finally the hour, minute, and second/*Serial.print(Clock.getHour(h12, PM), DEC);Serial.print(':');Serial.print(Clock.getMinute(), DEC);Serial.print(':');Serial.print(Clock.getSecond(), DEC);// Add AM/PM indicatorif (h12) {if (PM) {Serial.print(" PM ");} else {Serial.print(" AM ");}} else {Serial.print(" 24h ");}// Display the temperatureSerial.print("T=");Serial.print(Clock.getTemperature(), 2);// Tell whether the time is (likely to be) validif (Clock.oscillatorCheck()) {Serial.print(" O+");} else {Serial.print(" O-");}*/// Indicate whether an alarm went off/*if (Clock.checkIfAlarm(1)) {Serial.print(" A1!");}if (Clock.checkIfAlarm(2)) {Serial.print(" A2!");}*/// New line on display//Serial.print('\n'); // delay(1000);// Display Alarm 1 information/* Serial.print("Alarm 1: ");Clock.getA1Time(ADay, AHour, AMinute, ASecond, ABits, ADy, A12h, Apm);Serial.print(ADay, DEC);if (ADy) {Serial.print(" DoW");} else {Serial.print(" Date");}Serial.print(' ');Serial.print(AHour, DEC);Serial.print(' ');Serial.print(AMinute, DEC);Serial.print(' ');Serial.print(ASecond, DEC);Serial.print(' ');if (A12h) {if (Apm) {Serial.print('pm ');} else {Serial.print('am ');}}if (Clock.checkAlarmEnabled(1)) {Serial.print("enabled");}Serial.print('\n');// Display Alarm 2 informationSerial.print("Alarm 2: ");Clock.getA2Time(ADay, AHour, AMinute, ABits, ADy, A12h, Apm);Serial.print(ADay, DEC);if (ADy) {Serial.print(" DoW");} else {Serial.print(" Date");}Serial.print(' ');Serial.print(AHour, DEC);Serial.print(' ');Serial.print(AMinute, DEC);Serial.print(' ');if (A12h) {if (Apm) {Serial.print('pm');} else {Serial.print('am');}}if (Clock.checkAlarmEnabled(2)) {Serial.print("enabled");}*//* display alarm bitsSerial.print('\n');Serial.print('Alarm bits: ');Serial.print(ABits, DEC);*//*Serial.print('\n');Serial.print('\n');delay(1000);// Display the time once more as a test of the getTime() functionClock.getTime(year, month, date, DoW, hour, minute, second); Serial.print(year, DEC); Serial.print("/");Serial.print(month, DEC); Serial.print("/");Serial.print(date, DEC); Serial.print("day of the week :");Serial.println(DoW, DEC);Serial.print(hour, DEC); Serial.print(":");Serial.print(minute, DEC); Serial.print(":");Serial.println(second, DEC);*/
EthernetClient client = server.available(); if (client) { while (client.connected()) { if (client.available()) { char c = client.read(); if (readString.length() < 100) { readString += c; } if (c == '\n') { client.println("HTTP/1.1 200 OK"); client.println("Content-Type: text/html"); client.println(); client.println("<HTML>"); client.println("<BODY>"); client.println("<H1>Acende LED</H1>"); client.println("<hr />"); client.println("<br />"); client.println("<a href=\"/?ledon\"\">Ligar o led</a>"); client.println("<a href=\"/?ledoff\"\">Desligar o led</a><br />"); client.println("</BODY>"); client.println("</HTML>"); delay(1); client.stop(); if(readString.indexOf("?ledon") > 0) { digitalWrite(Pin, HIGH); } else { if(readString.indexOf("?ledoff") > 0) { digitalWrite(Pin, LOW); } } readString=""; } } } }}…