Comprei um DVR da marca Intelbras para gravar umas imagem e monitorar remotamente minha residencia dai tive a ideia de fazer uma placa que se conecte a este DVR e então acionar dispositivos ligados a rele desta placa de modo remoto.Para isto usei a saída RS-485 que controla a "Pan tilt"(aquelas câmeras que é possível movimentar sua posição).
A primeira coisa a fazer é saber qual é o protocolo utilizado! Então ao ver os tipos de comunicação utilizado achei muito material sobre o protocolo PELCO-D, uma vez com este protocolo montei o programa que será disponibilizado logo abaixo,no link para download que tem um manual e um software desenvolvido para configurar a placa depois de pronta:
https://drive.google.com/file/d/0Bx9JeN0dmzXxb1FPUm12MGxLQmc/edit?usp=sharing .
Coloquei também os arquivos gerber e um esquemático da placa além de ter um arquivo que foi feito no Kicad 2012 para gerar a placa, nesta pasta tem muita informação sobre o projeto.Para controlar os reles basta selecionar qual será a câmera configurada para utilizar a pan tilt e então pra cada comando da câmera irá acionar um rele(isso vai depender da configuração feita pelo software do projeto ),não testei em outros DVR´s que não seja o Intelbras.
Finalmente vou disponibilizar aqui o código completo do projeto, ficou gigante:
//***********************************************************************************************************
// acesso remoto com dvr
// versão : 1.0
// descrição : acesso remoto usando protocolo pelco D
// Autor : Aguivone
// micropcontrolador : ATmega 328P
// compilador : AVR-GCC / AtmelStudio 6.1
// Data do projeto : 10/12/2013
// finalizado : 20/12/2013
// EFUSE = 0XFF | LOCK BIT = 0XFF(sem cod. proteção) | FUSE HBIT = 0XDE | FUSE LBIT = 0XCF
//*************************************************************************************************************
#define F_CPU 16000000 // 16 MHz
#include <avr/io.h>
#include <avr/interrupt.h>
#include <stdio.h>
#include <util/delay.h>
#include <string.h>
#include <avr/eeprom.h> ///para usar a eeprom
#define ALTERNA_PINO(x,y) x ^= _BV(y)
#define LIGA_PINO(x,y) x |= _BV(y)
#define DESLIGA_PINO(x,y) x &= ~(_BV(y))
#define LED PD3
#define CONTROL_RS485 PD2
#define RELE1 PB1
#define RELE2 PB2
#define RELE3 PB3
#define RELE4 PB4
#define RELE5 PB5
#define RELE6 PC0
#define RELE7 PC1
char gcRs232_caracter;
long glTeste;
int gbRs232_pacote_recebido = 0;
int giRs232_pos=0;
//variaveis dos reles
char gcRele[7][6];//onde: byte 0=comando1_ligar,1=comando2_ligar,2=comando1_desligar,3=comando2_desligar,4=tempo,5='P' ou 'E'(pulso ou estado).
char gcEnd_placa = 0x01;//endereço da placa
char gcTemp_pulso[7];//tempo de pulso
char gcRs232_buffer[20];//o tamanho do buffer é 20 caracteres
char gcChecksum = 0;
char v1,v2; //variaveis de retorno
char convert_char[2];//variavels de retorno do tempo
int tempo_pisca=0;
int teste=0;
///////////////////////////////////////////////funçoes usadas pela usart////////////////////////////////////////////////
void RS485_inicializa(unsigned int BAUD)
{
unsigned int velocidade = F_CPU/16/BAUD-1;
//veja que na simulação do proteus dá erros nas velocidade diferentes de 9600 mas na pratica funciona.
UCSR0A = 0X20;
/* habilita receiver and transmitter buffers */
UBRR0H = (unsigned char)(velocidade>>8);
UBRR0L = (unsigned char)velocidade;
UCSR0B = 0X98;//deve se habilitar somente a interrupção de recepção para não travar o microcontrolador
/* 8 bits , 1 stop bit, assincrono , sem paridade,modo nornal */
UCSR0C = (1<<USBS0)|(3<<UCSZ00);
DESLIGA_PINO(PORTD,CONTROL_RS485);
}
void escreve(unsigned char carac)
{
LIGA_PINO(PORTD,CONTROL_RS485);//pino de controle da RS-485
_delay_loop_1(1);//tempo para estabilizar tensão
while ( !( UCSR0A & (1<<UDRE0)) ); //espera transmitir para enviar um novo
UDR0 = carac;
while ( !( UCSR0A & (1<<UDRE0)) ); //espera transmitir desabilita a transmissão
_delay_loop_1(20);//tempo para garantir o envio
DESLIGA_PINO(PORTD,CONTROL_RS485);//pino de controle da RS-485
}
void imprime(const char frase[20],int taman)
{
unsigned int indice=0;
while(indice<=taman)///veja que o programa pode travar se aqui não tiver as duas aspas
{
escreve(frase[indice]);
indice++;
}
}
////////////////controla os reles//////////////
void muda_rele(int rel,int estado)
{
switch(rel)
{
case 0:
{
if(estado == 1)
{
LIGA_PINO(PORTB,RELE1);
}
else
{
DESLIGA_PINO(PORTB,RELE1);
}
}break;
case 1:
{
if(estado == 1)
{
LIGA_PINO(PORTB,RELE2);
}
else
{
DESLIGA_PINO(PORTB,RELE2);
}
}break;
case 2:
{
if(estado == 1)
{
LIGA_PINO(PORTB,RELE3);
}
else
{
DESLIGA_PINO(PORTB,RELE3);
}
}break;
case 3:
{
if(estado == 1)
{
LIGA_PINO(PORTB,RELE4);
}
else
{
DESLIGA_PINO(PORTB,RELE4);
}
}break;
case 4:
{
if(estado == 1)
{
LIGA_PINO(PORTB,RELE5);
}
else
{
DESLIGA_PINO(PORTB,RELE5);
}
}break;
case 5:
{
if(estado == 1)
{
LIGA_PINO(PORTC,RELE6);
}
else
{
DESLIGA_PINO(PORTC,RELE6);
}
}break;
case 6:
{
if(estado == 1)
{
LIGA_PINO(PORTC,RELE7);
}
else
{
DESLIGA_PINO(PORTC,RELE7);
}
}break;
}
}
void rotina_rs485()
{
char conta=0;
while(conta < 7)
{
if(gcRele[conta][5]=='P')//esse rele é por pulso?
{
if((gcRele[conta][0] == gcRs232_buffer[2])&&(gcRele[conta][1] == gcRs232_buffer[3]))
{//verifica se o comando é pra dar pulso
muda_rele(conta,1);//liga o rele
gcTemp_pulso[conta] = gcRele[conta][4];
conta = 20;//com isso sai do laço imediatamente
}
}
else
{
if((gcRele[conta][0] == gcRs232_buffer[2])&&(gcRele[conta][1] == gcRs232_buffer[3]))
{//verifica se o comando é pra ligar?
muda_rele(conta,1);
conta = 20;//com isso sai do laço imediatamente
//escreve('L');
}
else{
if((gcRele[conta][2] == gcRs232_buffer[2])&&(gcRele[conta][3] == gcRs232_buffer[3]))
{//verifica se o comando é pra desligar?
muda_rele(conta,0);
conta = 20;//com isso sai do laço imediatamente
//escreve('D');
}
}
}
conta++;
}
}
void limpa_buffer()
{
int conta = 0;
while(conta<20)
{
gcRs232_buffer[conta]=0;
conta++;
}
giRs232_pos=0;
}
long char_to_long(char val1,char val2,char val3,char val4)
{
long retorno=0;
long gcValor3 = val4 -'0';//faz virar um long
long gcValor2 = val3 -'0';//faz virar um long
long gcValor1 = val2 -'0';//faz virar um long
long gcValor0 = val1 -'0';//faz virar um long
retorno = gcValor3+(gcValor2*10)+(gcValor1*100)+(gcValor0*1000);
return(retorno);
}
//=================================================
//funçao: _string_to_char
//parametros: char val1 - centena,char val2 - dezenas,char val3 - unidades
//retorno:
//=================================================
char string_to_char(char val2,char val3)
{
char retorno=0;
char Valor2 = val3 -'0';//faz virar um long
char Valor1 = val2 -'0';//faz virar um long
retorno = Valor2+(Valor1*10);
return(retorno);
}
char char_to_hex( char car)
{
char retorno;
switch(car)
{
case '0':
{
retorno = 0;
}break;
case '1':
{
retorno = 1;
}break;
case '2':
{
retorno = 2;
}break;
case '3':
{
retorno = 3;
}break;
case '4':
{
retorno = 4;
}break;
case '5':
{
retorno = 5;
}break;
case '6':
{
retorno = 6;
}break;
case '7':
{
retorno = 7;
}break;
case '8':
{
retorno = 8;
}break;
case '9':
{
retorno = 9;
}break;
case 'A':
{
retorno = 10;
}break;
case 'B':
{
retorno = 11;
}break;
case 'C':
{
retorno = 12;
}break;
case 'D':
{
retorno = 13;
}break;
case 'E':
{
retorno = 14;
}break;
case 'F':
{
retorno = 15;
}break;
}
return(retorno);
}
char hex_char( char car)
{
char retorno;
switch(car)
{
case 0x00:
{
retorno = '0';
}break;
case 0x01:
{
retorno = '1';
}break;
case 0x02:
{
retorno = '2';
}break;
case 0x03:
{
retorno = '3';
}break;
case 0x04:
{
retorno = '4';
}break;
case 0x05:
{
retorno = '5';
}break;
case 0x06:
{
retorno = '6';
}break;
case 0x07:
{
retorno = '7';
}break;
case 0x08:
{
retorno = '8';
}break;
case 0x09:
{
retorno = '9';
}break;
case 0x0A:
{
retorno = 'A';
}break;
case 0x0B:
{
retorno = 'B';
}break;
case 0x0C:
{
retorno = 'C';
}break;
case 0x0D:
{
retorno = 'D';
}break;
case 0x0E:
{
retorno = 'E';
}break;
case 0x0F:
{
retorno = 'F';
}break;
}
return(retorno);
}
char char_para_hexa(char vl1, char vl2)
{
char resultado = (char_to_hex(vl1)*16)+char_to_hex(vl2);
return(resultado);
}
void hexa_to_char(char hexad)
{
if(hexad > 0x0F)
{
v2= hexad&0X0F;
v2 = hex_char(v2);
v1 = (hexad>>4);
v1 = hex_char(v1);
}
else
{
v1='0';
v2=hex_char(hexad);
}
}
void int_to_char(int quant)
{
convert_char[0]='0';
convert_char[1]='0';
//convert_char[2]='0';
/* while(quant>=100)
{
quant=quant-100;
convert_char[0]++;
}*/
while(quant>=10)
{
quant=quant-10;
convert_char[0]++;
}
while(quant>=1)
{
quant=quant-1;
convert_char[1]++;
}
}
//////////////////////////interrupções requerida para usar a recepção serial///////////////////////////////////
ISR(USART_RX_vect)
{
gcRs232_caracter = (char)UDR0;
// if(giRs232_pos == 0)
if(((gcRs232_caracter == '<')||(gcRs232_caracter == 0XFF))&&(gbRs232_pacote_recebido == 0))//inicia a recepção se já tiver lido o buffer
{
//if(((gcRs232_caracter == '<')||(gcRs232_caracter == 0XFF))&&(gbRs232_pacote_recebido == 0))//inicia a recepção se já tiver lido o buffer
//{
gcRs232_buffer[0]=gcRs232_caracter;
gcChecksum=0;
giRs232_pos=1;
//}
}
else
{
if(giRs232_pos < 18)
{
if(gcRs232_buffer[0] == 0XFF)
{
if(giRs232_pos == 2)
{//pos2
if(gcRs232_buffer[1]!= gcEnd_placa)
{//já zera
gbRs232_pacote_recebido = 0;
limpa_buffer();
}
else
{//sim é igual
gcRs232_buffer[giRs232_pos]=gcRs232_caracter;
gcChecksum += gcRs232_caracter ;
giRs232_pos++;
// }
}
//}
}
else
{
gcRs232_buffer[giRs232_pos]=gcRs232_caracter;
if(giRs232_pos == 6)//se chegou até aqui é porque o endereço confere
{
if(gcChecksum == gcRs232_caracter )
{
gbRs232_pacote_recebido = 2;//sinaliza que tem dados
}
}
else
{
gcChecksum += gcRs232_caracter ;
}
giRs232_pos++;
}
}
else
{
if((gcRs232_caracter == '>')&&(gcRs232_buffer[0] == '<'))
{
gbRs232_pacote_recebido = 3;//sinaliza que tem dados
}
gcRs232_buffer[giRs232_pos]=gcRs232_caracter;
giRs232_pos++;
}
}
else
{//já zera pois pacote é muito grande,é erro
gbRs232_pacote_recebido = 0;
limpa_buffer();
}
}
}
//////////////////////////////////////ler e escreve um inteiro na eeprom//////////////////////////
void escreve_memoria(int ende,char dados)
{
_EEPUT(ende,dados);
}
char ler_memoria(int ende)
{
char temp;
_EEGET(temp,ende);//local a ser lido
return(temp);
}
void ler_memo()
{
gcEnd_placa = ler_memoria(1);
gcRele[0][4] = ler_memoria(2);//tempo
gcRele[1][4] = ler_memoria(3);
gcRele[2][4] = ler_memoria(4);
gcRele[3][4] = ler_memoria(5);
gcRele[4][4] = ler_memoria(6);
gcRele[5][4] = ler_memoria(7);
gcRele[6][4] = ler_memoria(8);
gcRele[0][5] = ler_memoria(9);//estado
gcRele[1][5] = ler_memoria(10);
gcRele[2][5] = ler_memoria(11);
gcRele[3][5] = ler_memoria(12);
gcRele[4][5] = ler_memoria(13);
gcRele[5][5] = ler_memoria(14);
gcRele[6][5] = ler_memoria(15);
gcRele[0][0] = ler_memoria(16);//comando1 ligar
gcRele[1][0] = ler_memoria(17);
gcRele[2][0] = ler_memoria(18);
gcRele[3][0] = ler_memoria(19);
gcRele[4][0] = ler_memoria(20);
gcRele[5][0] = ler_memoria(21);
gcRele[6][0] = ler_memoria(22);
gcRele[0][1] = ler_memoria(23);//comando2 ligar
gcRele[1][1] = ler_memoria(24);
gcRele[2][1] = ler_memoria(25);
gcRele[3][1] = ler_memoria(26);
gcRele[4][1] = ler_memoria(27);
gcRele[5][1] = ler_memoria(28);
gcRele[6][1] = ler_memoria(29);
gcRele[0][2] = ler_memoria(30);//comando1 desligar
gcRele[1][2] = ler_memoria(31);
gcRele[2][2] = ler_memoria(32);
gcRele[3][2] = ler_memoria(33);
gcRele[4][2] = ler_memoria(34);
gcRele[5][2] = ler_memoria(35);
gcRele[6][2] = ler_memoria(36);
gcRele[0][3] = ler_memoria(37);//comando2 desligar
gcRele[1][3] = ler_memoria(38);
gcRele[2][3] = ler_memoria(39);
gcRele[3][3] = ler_memoria(40);
gcRele[4][3] = ler_memoria(41);
gcRele[5][3] = ler_memoria(42);
gcRele[6][3] = ler_memoria(43);
}
void gravar_memoria(void)
{
cli();
escreve_memoria(1,gcEnd_placa);
escreve_memoria(16,gcRele[0][0]);//comando1 ligar
escreve_memoria(17,gcRele[1][0]);
escreve_memoria(18,gcRele[2][0]);
escreve_memoria(19,gcRele[3][0]);
escreve_memoria(20,gcRele[4][0]);
escreve_memoria(21,gcRele[5][0]);
escreve_memoria(22,gcRele[6][0]);
escreve_memoria(23,gcRele[0][1]);//comando2 ligar
escreve_memoria(24,gcRele[1][1]);
escreve_memoria(25,gcRele[2][1]);
escreve_memoria(26,gcRele[3][1]);
escreve_memoria(27,gcRele[4][1]);
escreve_memoria(28,gcRele[5][1]);
escreve_memoria(29,gcRele[6][1]);
escreve_memoria(30,gcRele[0][2]);//comando1 desligar
escreve_memoria(31,gcRele[1][2]);
escreve_memoria(32,gcRele[2][2]);
escreve_memoria(33,gcRele[3][2]);
escreve_memoria(34,gcRele[4][2]);
escreve_memoria(35,gcRele[5][2]);
escreve_memoria(36,gcRele[6][2]);
escreve_memoria(37,gcRele[0][3]);//comando1 desligar
escreve_memoria(38,gcRele[1][3]);
escreve_memoria(39,gcRele[2][3]);
escreve_memoria(40,gcRele[3][3]);
escreve_memoria(41,gcRele[4][3]);
escreve_memoria(42,gcRele[5][3]);
escreve_memoria(43,gcRele[6][3]);
escreve_memoria(2,gcRele[0][4]);//escreve tempo
escreve_memoria(3,gcRele[1][4]);//escreve tempo
escreve_memoria(4,gcRele[2][4]);//escreve tempo
escreve_memoria(5,gcRele[3][4]);//escreve tempo
escreve_memoria(6,gcRele[4][4]);//escreve tempo
escreve_memoria(7,gcRele[5][4]);//escreve tempo
escreve_memoria(8,gcRele[6][4]);//escreve tempo
escreve_memoria(9,gcRele[0][5]);//tipo de acionamento
escreve_memoria(10,gcRele[1][5]);
escreve_memoria(11,gcRele[2][5]);
escreve_memoria(12,gcRele[3][5]);
escreve_memoria(13,gcRele[4][5]);
escreve_memoria(14,gcRele[5][5]);
escreve_memoria(15,gcRele[6][5]);
escreve_memoria(50,'G');//indica que houve gravação
sei();
}
/////////////////////////////////config.padrao//////////////////////////
void padrao(void)
{
int ende = 0;
gcEnd_placa = 0x01;//endereço padrão é 01
while(ende < 7)//limpa registros
{
gcRele[ende][0]=0x00;//comando1 da pan ligar
gcRele[ende][1]=0x00;//comando2 da pan ligar
gcRele[ende][2]=0x00;//comando1 da pan desligar
gcRele[ende][3]=0x00;//comando2 da pan desligar
gcRele[ende][4]=0;//tempo
gcRele[ende][5]='E';//tipo de acionamento por estado do rele*/
ende++;
}
// seta registros
gcRele[0][1]=0x08;// sobe/comando2 da pan ligar
gcRele[0][3]=0x10;// desce/comando2 da pan desligar
gcRele[1][1]=0x04;// esquerda/comando2 da pan ligar
gcRele[1][3]=0x02;// direita/comando2 da pan desligar
gcRele[2][1]=0x20;// zoom+/comando2 da pan ligar
gcRele[2][3]=0x40;// zoom-/comando2 da pan desligar
gcRele[3][1]=0x80;// foco+/comando2 da pan ligar
gcRele[3][2]=0x01;// foco-/comando1 da pan desligar
gcRele[4][0]=0x02;// iris+/comando1 da pan ligar
gcRele[4][2]=0x04;// iris-/comando1 da pan desligar
gcRele[5][1]=0x0C;// ne/comando2 da pan ligar
gcRele[5][3]=0x12;// so/comando2 da pan desligar
gcRele[6][1]=0x0A;// no/comando2 da pan ligar
gcRele[6][3]=0x14;// se/comando2 da pan desligar
gravar_memoria();
}
void rotina_configura()
{
int erro = 1;
if((gcRs232_buffer[2]=='S')&&(giRs232_pos == 4))//envia todos os estados da placa
{
int cont=0;
escreve('<');
escreve('S');
while(cont<7)
{
unsigned char temp[13];
temp[0]=(char)'1'+cont;//numero do rele - assim compensa a não existencia do rele 0
hexa_to_char(gcRele[cont][0]);//comando ligar os rele B1
temp[1]= v1;
temp[2]= v2;
hexa_to_char(gcRele[cont][1]);//comando ligar os rele B2
temp[3]= v1;
temp[4]= v2;
hexa_to_char(gcRele[cont][2]);//comando desligar os rele B1
temp[5]= v1;
temp[6]= v2;
hexa_to_char(gcRele[cont][3]);//comando desligar os rele B2
temp[7]= v1;
temp[8]= v2;
int_to_char(gcRele[cont][4]);
// temp[9]= convert_char[0];
temp[9]= '0';
temp[10]= convert_char[0];
temp[11]= convert_char[1];
temp[12]= gcRele[cont][5];
temp[13]='>';
imprime(temp,13);
cont++;
}
hexa_to_char(gcEnd_placa);//endereço
escreve(v1);
escreve(v2);
escreve('>');
erro = 0;
}
if((gcRs232_buffer[2]=='P')&&(giRs232_pos == 4))//restaura todos os valores padrão
{
padrao();
imprime("<P>",2);
erro = 0;
}
if((gcRs232_buffer[2]=='E')&&(giRs232_pos == 6))//muda endereço do equipamento
{
gcEnd_placa = char_para_hexa(gcRs232_buffer[3],gcRs232_buffer[4]);//string_to_char('0',gcRs232_buffer[3],gcRs232_buffer[4]);
escreve_memoria(1,gcEnd_placa);
escreve_memoria(50,'G');
imprime("<M>",2);
erro = 0;
}
if((gcRs232_buffer[2]=='-')&&(giRs232_pos == 17))//comando de gravação
{
unsigned int ende = (int)gcRs232_buffer[3] - '0';//faz virar um long
ende--;//assim não vai existir rele 0
if(ende < 7 )
{
gcRele[ende][0]=char_para_hexa(gcRs232_buffer[4],gcRs232_buffer[5]);
gcRele[ende][1]=char_para_hexa(gcRs232_buffer[6],gcRs232_buffer[7]);
gcRele[ende][2]=char_para_hexa(gcRs232_buffer[8],gcRs232_buffer[9]);
gcRele[ende][3]=char_para_hexa(gcRs232_buffer[10],gcRs232_buffer[11]);
gcRele[ende][4]=string_to_char(gcRs232_buffer[13],gcRs232_buffer[14]);//tempo
gcRele[ende][5]=gcRs232_buffer[15];//tipo de acionamento*/
imprime("<C>",2);
erro = 0;
gravar_memoria();
}
else
{//rele inexistente
imprime("<E1>",3);
erro = 0;//não precisa escrever erro pois já foi escrito
}
}
if(erro > 0 )
{
imprime("<E2>",3);//falha qualquer
}
}
void led()
{
tempo_pisca = 0;
ALTERNA_PINO(PORTD,LED);
int contas=0;
while(contas<7)//verifica se tem algum rele pra desligar
{
if(gcTemp_pulso[contas] > 0)
{
if(gcTemp_pulso[contas]== 0x01)
{
muda_rele(contas,0); //desliga rele
}
gcTemp_pulso[contas]--;
}
contas++;
}
}
//////////////////////////////////////////////função principal///////////////////////////////////////////
int main(void)
{
DDRD = 0XFE; //inicializa portD como saida exceto pino D0(rx)
DDRB = 0XFF; //inicializa portB como saida
DDRC = 0XFF; //inicializa portC como saida
RS485_inicializa(9600);
if(ler_memoria(50)!= 'G')//testa se a memoria foi alterada alguma vez?
{//não
padrao();
escreve('P');
}
else
{//sim
ler_memo();//busca valores
escreve('G');
}
sei();//habilita interrupções
//////////////////////////
for(;;)
{
if(gbRs232_pacote_recebido > 0)
{//tem dados pra ler
cli(); //desabilita interrupções
if(gbRs232_pacote_recebido == 3)//config
{
if(gcRs232_buffer[1]=='C')//comando de gravação
{
rotina_configura();
}
}
if(gbRs232_pacote_recebido == 2)//config
{//dados da rs-485
rotina_rs485();
}
gbRs232_pacote_recebido = 0;//reseta buffer
limpa_buffer();
sei();//habilita interrupções
}
/////////////////////////////////
tempo_pisca++;
if(tempo_pisca == 13000)//alterna led a cada 100ms
{
led();
}
}
}