Tutorial: Shield RS485 e Conversor USB/RS485 com Arduino e SCADABR

Adquira estes componentes na Loja do Lab de Garagem

O shield contém um chip transceiver RS485 que faz o trabalho de conversão dos dados de TTL para RS485 e vice versa, LEDs para indicar o fluxo do tráfego na rede, bornes para ligar os cabos do barramento da rede, jumpers para selecionar uso de resistores de terminação e bias da rede e jumpers para escolher entre usar uma porta de comunicação serial física do ATmega do Arduino ou usar uma porta soft serial. Para a comunicação pela hard serial (HS) são utilizados os pinos digitais 0 e 1 do Arduino. Para a comunicação usando soft serial (SS) são usados os pinos digitais 2 e 3 do Arduino. O pino digital 6 é usado para comutação entre os modos de transmissão e recepção do transceiver do shield.

O conversor USB/RS485 usa o chip FT232RL para criar um dispositivo serial em qualquer microcomputador (porta COM) que pode ser acessada pelo usuário ou programas. O conversor também possui o mesmo circuito do transceiver RS485 com os jumpers, resistores, LEDs e bornes presente no shield.

Função dos Jumpers do Shield e do conversor:

  • JP1 - Liga ou desliga o resistor de pull up do barramento;

  • JP2 - Liga ou desliga o resistor de terminação do barramento;

  • JP3 - Liga ou desliga o resistor de pull down do barramento;

  • JP4 - Seleciona o RX da HS ou da SS;

  • JP5 - Seleciona o TX da HS ou da SS.

Observações:

  • JP1 e JP3 só podem ser ligados em um único dispositivo da rede e devem ser selecionados igualmente, os dois abertos ou os dois fechados.

  • JP2 só pode estar ligado se o dispositivo estiver em uma das duas extremidades do barramento, devendo obrigatoriamente ficar desligado quando estiver no meio da rede.

  • JP4 e JP5 devem ser selecionados igualmente, ou para HS ou para SS.

  • JP4 e JP5 não podem estar ligados em HS durante o upload de um sketch para o Arduino.

  • Os Pinos Digitais 0 e 1, ou 2 e 3 e o pino 6 não poderão ser utilizados por outro periférico.

  • O upload de um sketch sempre deve ser feito pelo canal USB nativo do Arduino, não sendo possível fazer-lo pela rede RS485.

Características técnicas

  • O Kit atende a norma Standard EIA RS-485 Physical media.

  • Opera com qualquer protocolo de comunicação serial com suporte a RS485 como o protocolo MODBUS RTU entre outros.

  • Taxa de transmissão suportada de 4800bps a 115200bps.

  • Topologia de rede ponto a ponto, ou multi-ponto com cabo de par trançado.

  • Modo de operação balanceado diferencial half duplex.

  • Até 32 dispositivos são suportados na mesma rede.

  • Comprimento máximo do cabo da rede de 1200m (4000 pés).

  • Níveis de tensão no barramento RS485 de -7 V a +12 V. Mark (1) tensões positivas (B-A > +200 mV) Space (0) tensões negativas (B-A < -200 mV).

  • O chip SN75176 é usado para a conversão TTL/RS485.

  • Utiliza os pinos digitais 0 e 1, ou 2 e 3 do Arduino para comunicação serial com o shield e o pino digital 6 para comutação entre os modos de transmissão e recepção. Obs. Os Pinos Digitais 0 e 1, ou 2 e 3 e o pino 6 não poderão ser utilizados por outro periférico.

  • A alimentação do shield é feita através dos pinos 5 V e GND da placa do Arduino.

  • Disponibiliza os pinos do Arduino em dois barramentos laterais, facilitando aplicações que possam utilizar os pinos de I/O do módulo.

  • O conversor USB/RS485 utiliza o chip FTD232RL para a conversão de USB para TTL e é alimentado pela porta USB do computador.

 

Instalação do Shield no Arduino

  1. Encaixar o shield conversor RS485/TTL ao Arduino como na figura 2;

  2. Ligar o cabo positivo ou A do barramento da rede RS485 ao borne A do conversor RS485/TTL usando uma chave de fenda;

  3. Ligar o cabo negativo ou B do barramento da rede RS485 ao borne B do conversor RS485/TTL usando uma chave de fenda;

  4. Ligar o borne GND à malha do cabo do barramento;

  5. Abra os jumpers JP4 e JP5;

  6. Selecione o sketch desejado na IDE do Arduino e faça o upload para o Arduino usando o cabo USB do Arduino, sempre verifique que JP4 e JP5 estão abertos antes de fazer o upload;

  7. Religue os jumpers JP4 e JP5 selecionando para HS ou SS, conforme esteja no sketch usado no Arduino;

  8. Ligar os jumpers JP1, JP2 e JP3 conforme a necessidade, ver as observações na seção Descrição do KIT para mais detalhes.

Instalação do conversor USB/RS485 no PC

  1. Ligar o cabo positivo ou A do barramento da rede RS485 ao borne A do conversor USB/ RS485 usando uma chave de fenda;

  2. Ligar o cabo negativo ou B do barramento da rede RS485 ao borne B do conversor USB/ RS485;

  3. Ligar o borne GND à malha do cabo do barramento;

  4. Ligar o cabo USB ao conector USB do conversor e a uma porta USB livre do PC;

  5. Aguarde o PC reconhecer o novo dispositivo e quando abrir a tela pedindo a indicação do caminho para o drive de instalação, aponte para a pasta do PC onde está instalado a IDE do Arduino e procure pela pasta FTDI USB Drivers dentro da pasta DRIVERS, normalmente será algo parecido com: X:\arduinoYYY\drivers;
  6. Após a instalação ser concluída, verifique qual porta foi criada usando o gerenciador de dispositivos do Windows.

 

Configuração

  • O usuário deve configurar igualmente todos os parâmetros das portas de comunicação de todos os dispositivos ligados a rede RS485, observando os valores de bits por segundo (baud rate), número de bits dos dados transmitidos, paridade e stop bits.

  • Todos os dispositivos devem estar usando o mesmo protocolo de comunicação.

  • O pino digital (6) do Arduino terá função de enable (EN) que habilita o conversor RS485/TTL para o modo de transmissão quando estiver em nível lógico 1 (5V) ou para modo de recepção quando o pino de enable estiver em nível lógico 0 (0V).

  • Os LEDs TX e RX do shield e do conversor indicam o tráfego da rede.

Teste Inicial

Para testar o funcionamento correto dos módulos e a comunicação entre o PC e o Arduino a forma mais simples é usando o SERIAL MONITOR que está disponível na IDE do Arduino e montando a aplicação a seguir.

 

De posse das ferramentas e dos componentes, siga os passos abaixo:

  1. Instalar o shield ao Arduino e o conversor ao PC conforme mostrado nos itens instalação e configuração;

  2. Montar o circuito nos conectores do shield usando os componentes conforme mostrado na figura 4.

  3. Ligar o potenciômetro de 10k com o cursor ligado a entrada analógica 0 e os pinos externos ligados ao GND e 5V;

  4. Ligar a chave táctil entre o 5V e a entrada digital 4 com resistor de pull down de 10k;

ATENÇÃO: Revisar todas as ligações com muito cuidado antes de energizar o circuito, para evitar possíveis danos aos aparelhos!!!

  1. Ligar uma fonte de alimentação de 7 a 12 VCC ao conector de alimentação externa do Arduino.

  2. Abrir um novo sketch na IDE do Arduino e copiar o texto do apêndice III

  3. Faça o upload para o Arduino conectando o cabo USB no conector do Arduino e a uma porta USB do PC. Não se esqueça de desligar os jumpers JP4 e JP5 durante o upload do sketch religando-os em seguida;

  4. Assim que a mensagem confirmando que o upload terminou corretamente, desligue o cabo USB do Arduino e feche a IDE do Arduino;

  5. Ligue o conversor USB/RS485 ao PC e reabra a IDE do Arduino, abra o sketch do teste e na aba Tools / Serial Port selecione a porta COM do conversor USB/RS485.
  6. Neste sketch o Arduino ficará aguardando o recebimento de um caractere digitado no teclado do PC pelo usuário que deve estar usando um programa de terminal como por exemplo o Serial Monitor da IDE do Arduino. Assim que for recebido um caractere, o Arduino irá enviar de volta ao PC o valor presente na entrada analógica A0 (variando de 0 a 255), o estado da entrada digital D4 (0 ou 1) e o caractere que foi digitado;
  7. Abra o SERIAL MONITOR na IDE do Arduino, e ajuste a velocidade para 9600 baud. Agora digite a tecla “a” e pressione a tecla Send ( ou tecle ENTER), se tudo estiver correto, você terá uma tela parecida com a da figura 5. Faça o teste com outras teclas, mas digite e envie apenas uma de cada vez e aguarde o retorno do Arduino, não se esqueça que o RS485 é half duplex, e o seu dedo é o protocolo de comunicação.
  8. Teste a comunicação com outras velocidades mudando o baud rate no sketch na linha Serial.begin(9600); faça o novo upload repetindo os passos de 7 a 11 e mudando também a velocidade no Serial Monitor.

 

Passe seguinte programação para o Arduino:

/*

Teste de Comunicação Serial com RS485

Este exemplo é de domínio público

Testado na IDE 1.0.1

*/

int POTENCIOMETRO = 0; // valor lido no potenciometro na entrada analogica A0

int CHAVE = 0; // Estado do push botton ligado na entrada D4

int RECEBIDO = 0; // Byte recebido pela serial

void setup()

{

// Iniciando a comunicação a 9600 bps:

Serial.begin(9600);

pinMode(4, INPUT); // Chave no pino digital 4

pinMode(6, OUTPUT); // TX ENABLE no pino digital 6 (está no SHIELD)

}

void loop()

{

// Verifica se recebeu algum caracter pela serial

if (Serial.available() > 0) {

// le o byte recebido:

RECEBIDO = Serial.read();

// le a entrada analógica e divide por 4 para caber em um byte (0-255):

POTENCIOMETRO = analogRead(A0)/4;

// le o estado do botão:

CHAVE = map(digitalRead(4), 0, 1, 0, 1);

// envia os valores dos sensores:

digitalWrite(6, HIGH); // coloca o Shield RS485 em modo de transmissão

delay(1); // aguarda 1 milisegundo

Serial.write("Pino A0 = ");

Serial.print(POTENCIOMETRO);//Serial.println(POTENCIOMETRO, DEC);

Serial.write('\n');

Serial.write("Pino D4 = ");

Serial.print(CHAVE);

Serial.write('\n');

Serial.write("Caracter recebido = ");

Serial.write((byte)RECEBIDO);//Serial.write(RECEBIDO);//Serial.println(RECEBIDO, BYTE);//Serial.write(byte(RECEBIDO));

Serial.write('\n');

delay(100);

digitalWrite(6, LOW); // coloca o Shield RS485 em modo de recepção

Serial.flush();

delay(300); // aguarda

}

}

Se não estiver funcionando como o esperado:

  • Revise todas as ligações do circuito;

  • A tensão da fonte de alimentação do Arduino;

  • A ligação dos cabos do barramento RS485;

  • Verifique se a porta COM do conversor USB é a que foi selecionada na IDE do Arduino;

  • Se o baud rate no serial monitor está em 9600;

  • Se o drive do FTD232RL foi corretamente instalado no gerenciador de dispositivos de hardware do PC.

Estes produtos podem ser utilizados com SCADABR para automação residencial, utilizando o Arduino como MODBUS e assim podendo acessar remotamente periféricos instalados a ele. Junto com o Arduino é possível fazer leituras de sensores, ativar ou desativar periféricos (lâmpada, portões eletrônicos, etc).

O SCADABR é um software de sistema supervisório Open Source e pode ser utilizado para automação em vários ambientes como indústrias, residências, sistemas de energia e etc.

Para utilizar o Shield RS485 e o Conversor USB/RS485 com ScadaBR, baixe o manual clicando aqui.

E é isso! Esperamos que tenha gostado! Caso tenha dúvidas, poste aqui neste blog! Se tiver sugestões para tutoriais, poste aqui. Para ver outros tutoriais e projetos desenvolvidos pela equipe LdG e por outros garagistas, clique aqui e aqui, respectivamente

Referências:

http://arduino.cc/en/

http://www.scadabr.com.br/

Exibições: 30465

Comentar

Você precisa ser um membro de Laboratorio de Garagem (arduino, eletrônica, robotica, hacking) para adicionar comentários!

Entrar em Laboratorio de Garagem (arduino, eletrônica, robotica, hacking)

Comentário de Claudio Gularte Junior em 2 junho 2013 às 22:35

Pessoal do Lab de garagem, pode disponibilizar os códigos em txt?

Fernando, seu código não funcionou aqui. (Só recortei e colei.)

Comentário de Fernando Nunes em 1 junho 2013 às 23:31

Meu sketch ficou assim:

/* Parâmetros Modbus RTU de comunicação, o Mestre e os escravos devem usar os mesmos
parâmetros */
void configure_mb_slave(long baud, char parity, char txenpin);
int update_mb_slave(unsigned char slave, int *regs,
unsigned int regs_size);

enum {
COMM_BPS = 9600, /* baud rate */
MB_SLAVE = 1, /* endereço do escravo modbus */
/* cada escravo modbus deve ter um */
/* endereço único na rede modbus */
PARITY = 'n', /* paridade */
TXEN = 6 /*Definir o pino usado para colocar o driver RS485 em modo de transmissão, utilizado somente em redes RS485 quando colocar em 0 ou 1 para redes RS232 */
};
/* registros do escravo (holding registers)
*
* Aqui ficam ordenados todos os registros de leitura e escrita
* da comunicação entre o mestre e o escravo (SCADA e arduino)
*
*/
enum {
MB_PINO_5, /* Controle do Led no pino 3 (desliga=0 liga=1) offset 0 (p/ data point no scadabr)*/
MB_PINO_4, /* Leitura da chave no pino 4 (desliga=0 liga=1) offset 1 (p/ data point no scadabr)*/
MB_A0, /* Leitura da entrada analógica 0 (0 a 1023) offset 2 (p/ data point no scadabr)*/
MB_REGS /* número total de registros do escravo */
};
int regs[MB_REGS];
int ledPin5 = 5;
int ledPin13 = 13;
int buttonPin4 = 4;
int buttonState = 0; // variável para ler e estado do botão
unsigned long wdog = 0; /* watchdog */
unsigned long tprev = 0; /* tempo anterior do último comando*/
unsigned long tanalogprev = 0; /* tempo anterior da leitura dos pinos analógicos*/
void setup()
{
/* configura cominicação modbus
* 9600 bps, 8N1, RS485 network */
        configure_mb_slave(COMM_BPS, PARITY, TXEN);
        

        pinMode(3,OUTPUT);
        pinMode(4,OUTPUT);
        
        pinMode(5,INPUT);
        pinMode(6,INPUT);
}
void loop()
{
/* verifica se há solicitações do mestre */
if(update_mb_slave(MB_SLAVE, regs, MB_REGS))
wdog = millis();
if ((millis() - tanalogprev) > 1000) { /* atualiza as entradas analogica a cada 1 segundo */
regs[MB_A0] = analogRead(0); /* ler entrada analógica 0 e salva valor no registro modbus*/
tanalogprev = millis();
}
buttonState = digitalRead(buttonPin4); // ler estado da chave no pino 4
regs[MB_PINO_4] = buttonState; // salva valor no registro modbus
if (buttonState == HIGH) { // caso a chave esteja pressionada
digitalWrite(ledPin13, HIGH); // liga o Led do pino 13
}
else { // caso a chave não esteja pressionada
digitalWrite(ledPin13, LOW); // desliga o Led do pino 13
}
/* os valores do registro MB_PINO_5 é definido pelo mestre modbus (SCADA) */
switch(regs[MB_PINO_5]) {
case 1:
digitalWrite(ledPin5, HIGH);
break;
default: /* apagado */
digitalWrite(ledPin5, LOW);
}
}
/****************************************************************************
* INICIO DAS FUNÇÕES ESCRAVO Modbus RTU
****************************************************************************/
/* variaveis globais */
unsigned int Txenpin = TXEN; /*Definir o pino usado para colocar o driver RS485 em modo de transmissão, utilizado somente em redes RS485 quando colocar em 0 ou 1 para redes RS232 */
/* Lista de códigos de função modbus suportados. Se você implementar um novo, colocar o seu código de função aqui! */
enum{
FC_READ_REGS = 0x03, /*Ler um bloco contiguo de registros*/
FC_WRITE_REG = 0x06, /*Escrever em um único registro*/
FC_WRITE_REGS = 0x10 /*Escrever em um bloco contíguo de registos*/
};
/* Funções suportadas. Se você implementar um novo, colocar seu código em função nessa matriz! */
const unsigned char fsupported[] = {
FC_READ_REGS, FC_WRITE_REG, FC_WRITE_REGS };
/* constantes */
enum {
MAX_READ_REGS = 0x7D,
MAX_WRITE_REGS = 0x7B,
MAX_MESSAGE_LENGTH = 256
};
enum {
RESPONSE_SIZE = 6,
EXCEPTION_SIZE = 3,
CHECKSUM_SIZE = 2
};
/* código de exceções */
enum {
NO_REPLY = -1,
EXC_FUNC_CODE = 1,
EXC_ADDR_RANGE = 2,
EXC_REGS_QUANT = 3,
EXC_EXECUTE = 4
};
/* posições dentro da matriz de consulta / resposta */
enum {
SLAVE = 0,
FUNC,
START_H,
START_L,
REGS_H,
REGS_L,
BYTE_CNT
};
/*
CRC
INPUTS:
buf -> Matriz contendo a mensagem a ser enviada para o controlador mestre.
start -> Início do loop no crc do contador, normalmente 0.
cnt -> Quantidade de bytes na mensagem a ser enviada para o controlador mestre
OUTPUTS:
temp -> Retorna byte crc para a mensagem.
COMMENTÁRIOS:
Esta rotina calcula o byte crc alto e baixo de uma mensagem.
Note que este CRC é usado somente para Modbus, não em Modbus PLUS ou TCP.
****************************************************************************/
unsigned int crc(unsigned char *buf, unsigned char start,
unsigned char cnt)
{
unsigned char i, j;
unsigned temp, temp2, flag;
temp = 0xFFFF;
for (i = start; i < cnt; i++) {temp = temp ^ buf[i];
for (j = 1; j <= 8; j++) {flag = temp & 0x0001;
temp = temp >> 1;
if (flag)temp = temp ^ 0xA001;
}
}
/* Inverter a ordem dos bytes. */
temp2 = temp >> 8;
temp = (temp 8) | temp2;
temp &= 0xFFFF;
return (temp);
}
/***********************************************************************
*
* As seguintes funções constroem o frame de
* um pacote de consulta modbus.
*
***********************************************************************/
/*
* Início do pacote de uma resposta read_holding_register
*/
void build_read_packet(unsigned char slave, unsigned char function,
unsigned char count, unsigned char *packet)
{
packet[SLAVE] = slave;
packet[FUNC] = function;
packet[2] = count * 2;
}
/*
* Início do pacote de uma resposta preset_multiple_register
*/
void build_write_packet(unsigned char slave, unsigned char function,
unsigned int start_addr,
unsigned char count,
unsigned char *packet)
{
packet[SLAVE] = slave;
packet[FUNC] = function;
packet[START_H] = start_addr >> 8;
packet[START_L] = start_addr & 0x00ff;
packet[REGS_H] = 0x00;
packet[REGS_L] = count;
}
/*
* Início do pacote de uma resposta write_single_register
*/
void build_write_single_packet(unsigned char slave, unsigned char function,
unsigned int write_addr, unsigned int reg_val, unsigned char* packet)
{
packet[SLAVE] = slave;
packet[FUNC] = function;
packet[START_H] = write_addr >> 8;
packet[START_L] = write_addr & 0x00ff;
packet[REGS_H] = reg_val >> 8;
packet[REGS_L] = reg_val & 0x00ff;
}
/*
* Início do pacote de uma resposta excepção
*/
void build_error_packet(unsigned char slave, unsigned char function,
unsigned char exception, unsigned char *packet)
{
packet[SLAVE] = slave;
packet[FUNC] = function + 0x80;
packet[2] = exception;
}
/*************************************************************************
*
* modbus_query( packet, length)
*
* Função para adicionar uma soma de verificação para o fim de um pacote.
* Por favor, note que a matriz pacote deve ser de pelo menos 2 campos mais do que
* String_length.
**************************************************************************/
void modbus_reply(unsigned char *packet, unsigned char string_length)
{
int temp_crc;temp_crc = crc(packet, 0, string_length);
packet[string_length] = temp_crc >> 8;
string_length++;
packet[string_length] = temp_crc & 0x00FF;
}
/***********************************************************************
*
* send_reply( query_string, query_length )
*
* Função para enviar uma resposta a um mestre Modbus.
* Retorna: o número total de caracteres enviados
************************************************************************/
int send_reply(unsigned char *query, unsigned char string_length)
{
unsigned char i;
if (Txenpin > 1) { // coloca o MAX485 no modo de transmissão
UCSR0A=UCSR0A |(1 TXC0);
digitalWrite( Txenpin, HIGH);
delayMicroseconds(3640); // aguarda silencio de 3.5 caracteres em 9600bps
}
modbus_reply(query, string_length);
string_length += 2;
for (i = 0; i < string_length; i++) {
Serial.write(byte(query[i]));
}
if (Txenpin > 1) {// coloca o MAX485 no modo de recepção
while (!(UCSR0A & (1 TXC0)));
digitalWrite( Txenpin, LOW);
}
return i; /* isso não significa que a gravação foi bem sucedida */
}
/***********************************************************************
*
* receive_request( array_for_data )
*
* Função para monitorar um pedido do mestre modbus.
*
* Retorna: Número total de caracteres recebidos se OK
* 0 se não houver nenhum pedido
* Um código de erro negativo em caso de falha
***********************************************************************/
int receive_request(unsigned char *received_string)
{
int bytes_received = 0;
/* FIXME: não Serial.available esperar 1.5T ou 3.5T antes de sair do loop? */
while (Serial.available()) {
received_string[bytes_received] = Serial.read();
bytes_received++;
if (bytes_received >= MAX_MESSAGE_LENGTH)
return NO_REPLY; /* erro de porta */
}
return (bytes_received);
}
/*********************************************************************
*
* modbus_request(slave_id, request_data_array)
*
* Função que é retornada quando o pedido está correto
* e a soma de verificação está correto.
* Retorna: string_length se OK
* 0 se não
* Menos de 0 para erros de exceção
*
* Nota: Todas as funções usadas para enviar ou receber dados via
* Modbus devolver esses valores de retorno.
*
**********************************************************************/
int modbus_request(unsigned char slave, unsigned char *data)
{
int response_length;
unsigned int crc_calc = 0;
unsigned int crc_received = 0;
unsigned char recv_crc_hi;
unsigned char recv_crc_lo;
response_length = receive_request(data);
if (response_length > 0) {
crc_calc = crc(data, 0, response_length - 2);
recv_crc_hi = (unsigned) data[response_length - 2];
recv_crc_lo = (unsigned) data[response_length - 1];
crc_received = data[response_length - 2];
crc_received = (unsigned) crc_received 8;
crc_received =
crc_received | (unsigned) data[response_length - 1];
/*********** verificar CRC da resposta ************/
if (crc_calc != crc_received) {
return NO_REPLY;
}/* verificar a ID do escravo */
if (slave != data[SLAVE]) {
return NO_REPLY;
}
}
return (response_length);
}
/*********************************************************************
*
* validate_request(request_data_array, request_length, available_regs)
*
* Função para verificar se o pedido pode ser processado pelo escravo.
*
* Retorna: 0 se OK
* Um código de exceção negativa em caso de erro
*
**********************************************************************/
int validate_request(unsigned char *data, unsigned char length,
unsigned int regs_size)
{
int i, fcnt = 0;
unsigned int regs_num = 0;
unsigned int start_addr = 0;
unsigned char max_regs_num;
/* verificar o código de função */
for (i = 0; i < sizeof(fsupported); i++) {
if (fsupported[i] == data[FUNC]) {
fcnt = 1;
break;
}
}
if (0 == fcnt)
return EXC_FUNC_CODE;
if (FC_WRITE_REG == data[FUNC]) {
/* Para a função de escrever um reg único, este é o registro alvo.*/
regs_num = ((int) data[START_H] 8) + (int) data[START_L];
if (regs_num >= regs_size)
return EXC_ADDR_RANGE;
return 0;
}
/* Para as funções de leitura / escrita de registros, este é o intervalo. */
regs_num = ((int) data[REGS_H] 8) + (int) data[REGS_L];
/* verifica a quantidade de registros */
if (FC_READ_REGS == data[FUNC])
max_regs_num = MAX_READ_REGS;
else if (FC_WRITE_REGS == data[FUNC])
max_regs_num = MAX_WRITE_REGS;
if ((regs_num < 1) || (regs_num > max_regs_num))
return EXC_REGS_QUANT;
/* verificará a quantidade de registros, endereço inicial é 0 */
start_addr = ((int) data[START_H] 8) + (int) data[START_L];
        if ((start_addr + regs_num) > regs_size)
                return EXC_ADDR_RANGE;

        return 0;         /* OK, no exception */
}
/************************************************************************
*
* write_regs(first_register, data_array, registers_array)
*
* escreve nos registradores do escravo os dados em consulta,
* A partir de start_addr.
*
* Retorna: o número de registros escritos
************************************************************************/
int write_regs(unsigned int start_addr, unsigned char *query, int *regs)
{
        int temp;
        unsigned int i;

        for (i = 0; i < query[REGS_L]; i++) {
                /* shift reg hi_byte to temp */
                temp = (int) query[(BYTE_CNT + 1) + i * 2] 8;
                /* OR with lo_byte           */
                temp = temp | (int) query[(BYTE_CNT + 2) + i * 2];

                regs[start_addr + i] = temp;
        }
        return i;
}
/************************************************************************
*
* preset_multiple_registers(slave_id, first_register, number_of_registers,
* data_array, registers_array)
*
* Escreva os dados na matriz dos registos do escravo.
*
*************************************************************************/
int preset_multiple_registers(unsigned char slave,
unsigned int start_addr,
unsigned char count,
unsigned char *query,
int *regs)
{
unsigned char function = FC_WRITE_REGS; /* Escrever em múltiplos registros */
int status = 0;
unsigned char packet[RESPONSE_SIZE + CHECKSUM_SIZE];
build_write_packet(slave, function, start_addr, count, packet);
if (write_regs(start_addr, query, regs)) {
status = send_reply(packet, RESPONSE_SIZE);
}
return (status);
}
/************************************************************************
*
* write_single_register(slave_id, write_addr, data_array, registers_array)
*
* Escrever um único valor inteiro em um único registo do escravo.
*
*************************************************************************/
int write_single_register(unsigned char slave,
unsigned int write_addr, unsigned char *query, int *regs)
{
unsigned char function = FC_WRITE_REG; /* Função: Write Single Register */
int status = 0;
unsigned int reg_val;
unsigned char packet[RESPONSE_SIZE + CHECKSUM_SIZE];
reg_val = query[REGS_H] 8 | query[REGS_L];
build_write_single_packet(slave, function, write_addr, reg_val, packet);
regs[write_addr] = (int) reg_val;
/*
written.start_addr=write_addr;
written.num_regs=1;
*/
status = send_reply(packet, RESPONSE_SIZE);
return (status);
}
/************************************************************************
*
* read_holding_registers(slave_id, first_register, number_of_registers,
* registers_array)
*
* lê os registros do escravo e envia para o mestre Modbus
*
*************************************************************************/
int read_holding_registers(unsigned char slave, unsigned int start_addr,
unsigned char reg_count, int *regs)
{
unsigned char function = 0x03; /* Função 03: Read Holding Registers */
int packet_size = 3;
int status;
unsigned int i;
unsigned char packet[MAX_MESSAGE_LENGTH];
build_read_packet(slave, function, reg_count, packet);
for (i = start_addr; i < (start_addr + (unsigned int) reg_count);
i++) {
packet[packet_size] = regs[i] >> 8;
packet_size++;
packet[packet_size] = regs[i] & 0x00FF;
packet_size++;
}
status = send_reply(packet, packet_size);
return (status);
}
void configure_mb_slave(long baud, char parity, char txenpin)
{
Serial.begin(baud);
switch (parity) {
case 'e': // 8E1
UCSR0C |= ((1UPM01) | (1UCSZ01) | (1UCSZ00));
// UCSR0C &= ~((1UPM00) | (1UCSZ02) | (1USBS0));
break;
case 'o': // 8O1
UCSR0C |= ((1UPM01) | (1UPM00) | (1UCSZ01) | (1UCSZ00));
// UCSR0C &= ~((1UCSZ02) | (1USBS0));
break;
case 'n': // 8N1
UCSR0C |= ((1UCSZ01) | (1UCSZ00));
// UCSR0C &= ~((1UPM01) | (1UPM00) | (1UCSZ02) | (1USBS0));
break;
default:
break;
}
if (txenpin > 1) { // pino 0 & pino 1 são reservados para RX/TX
Txenpin = txenpin; /* definir variável global */
pinMode(Txenpin, OUTPUT);
digitalWrite(Txenpin, LOW);
}
return;
}
/*
* update_mb_slave(slave_id, holding_regs_array, number_of_regs)
*
* verifica se há qualquer pedido válido do mestre modbus. Se houver,
* executa a ação solicitada
*/
unsigned long Nowdt = 0;
unsigned int lastBytesReceived;
const unsigned long T35 = 5;
int update_mb_slave(unsigned char slave, int *regs,
unsigned int regs_size)
{
unsigned char query[MAX_MESSAGE_LENGTH];
unsigned char errpacket[EXCEPTION_SIZE + CHECKSUM_SIZE];
unsigned int start_addr;
int exception;
int length = Serial.available();
unsigned long now = millis();
if (length == 0) {
lastBytesReceived = 0;
return 0;
}
if (lastBytesReceived != length) {
lastBytesReceived = length;
Nowdt = now + T35;
return 0;
}
if (now < Nowdt)
return 0;
lastBytesReceived = 0;
length = modbus_request(slave, query);
if (length < 1)
return length;
exception = validate_request(query, length, regs_size);
if (exception) {
build_error_packet(slave, query[FUNC], exception,
errpacket);
send_reply(errpacket, EXCEPTION_SIZE);
return (exception);
}
start_addr = ((int) query[START_H] 8) +
(int) query[START_L];
switch (query[FUNC]) {
case FC_READ_REGS:
return read_holding_registers(slave,
start_addr,
query[REGS_L],
regs);
break;
case FC_WRITE_REGS:
return preset_multiple_registers(slave,
start_addr,
query[REGS_L],
query,
regs);
break;
case FC_WRITE_REG:
write_single_register(slave,
start_addr,
query,
regs);
break;
}
}

Comentário de Fernando Nunes em 1 junho 2013 às 23:29

Pois é o meu tava dando erro ao compilar, fiz algumas correções e a compilação foi feita só que não to conseguindo comunicar com Scadabr, pode ser que no meu caso seja esses jumpers.

Comentário de Claudio Gularte Junior em 1 junho 2013 às 17:58

E ai galera, alguém pode me conseguir o código do apêndice IV (Sketch para teste com o SCADABR e Arduino escravo MODBUS) do manual? 

Comentário de Fernando Nunes em 21 maio 2013 às 12:52

Obrigado! Funcionou.

Comentário de Laboratório de Garagem em 21 maio 2013 às 11:00

Para teste inicial coloque os jumpers no HS e Serial. Coloque o resistor de pull-up ou pull-down (apenas um dos dois).

Com isto já deve funcionar.

Comentário de Fernando Nunes em 20 maio 2013 às 16:04

Alguem poderia me dizer qual a posição dos jumpers no teste inicial?

Comentário de Raphael Ribeiro Muzzi França em 6 maio 2013 às 17:41

Pessoal,

É possível usar uma leitora que trabalha com protocolo Wiegand (RS485) para fazer o controle de acesso de uma porta por exemplo. Quero que o arduino controle os crachás que podem ou não acessar aquele ambiente.

É possível?

Comentário de Claudio Gularte Junior em 14 abril 2013 às 22:14

Wiechert,

Tem como postar o link do código que você usou para o ScadaBR no Manual do Kit RS485? ( Codigo do Apêndice III).

Vlw!

Comentário de Wiechert em 3 fevereiro 2013 às 18:59

Francisco, você não viu as as instruções do item  instalação do conversor USB/RS485 no PC, no passo 5 é explicado como instalar o drive do conversor, que não precisa ser baixado já que o mesmo está na pasta de drivers da IDE do arduino:

5- Aguarde o PC reconhecer o novo dispositivo e quando abrir a tela pedindo a indicação do caminho para o drive de instalação, aponte para a pasta do PC onde está instalado a IDE do Arduino e procure pela pasta FTDI USB Drivers dentro da pasta DRIVERS, normalmente será algo parecido com: X:\arduinoYYY\drivers;

6- Após a instalação ser concluída, verifique qual porta foi criada usando o gerenciador de dispositivos do Windows.

O tutorial está completo, mas para que as coisas funcionem, é preciso seguir todos os passos do começo ao fim.

Para trabalhar com o SCADABR veja o manual do shield na página da loja: http://www.labdegaragem.com.br/loja/ManualdoUsu%C3%A1rio.2RS485.pdf

Tente novamente que com certeza você vai conseguir fazer funcionar.

© 2024   Criado por Marcelo Rodrigues.   Ativado por

Badges  |  Relatar um incidente  |  Termos de serviço