Alguém conhece o scadabr e sabe como usa-lo, caso conheça por favor entre em contato pois preciso de algumas informações.

Uma delas é o protocolo modbus pois queria saber como comunicar um sensor,atuador,etc como o supervisor por meio de um microcontrolador ou coisa paracida, me parece que para poder acionar ou receber informações o supervisorio precisa de um clp (que é caro) ou por um microcontrolador?

se alguém tiver informações sobre como usar e implementar ou pelo menos mostrar o caminho das pedras já agradeço.

adriano

Exibições: 97575

Responder esta

Respostas a este tópico

Oi Kleber, desculpe pela demora na resposta, como ainda to focado no RS485 , que ainda não resolvi, mas logo vou resolver,fiquei um pouco desligado do modbus, se o seu problema é só resolver uma questão de comunicação serial, do tipo, usando o cabo usb que vem junto com arduino por meio do drive FTDI que vc ja deve ter instalado e agora vc faz os uploads do seu arduino, o melhor é  fazer o sketch, vou te mandar um sketch que deve funcionar direitinho com o scadabr e ai vc vai poder fazer alguns testes do tipo que ja foi feito por aqui.

detalhe tenha mente que a sua IDE deve ser o 0.23 do arduino e não o 1.0 pois o sketch que temos só funciona com o 0.23

depois de resolver essa comunicação com scadabr, te indico pra vc entrar na linha do Rs485, pois deste modo realmente vc vai poder montar uma rede com varios sensores controlados pelo scadabr(que é o que quero fazer no momento, amanha ta chegando os meus plugs ai devo retestar a minha rede.

aqui está um sketch funcionando no serial do arduino(antes de te mandar eu testei, o teste é simples é só criar o datasourcee o datapoint no scadabr relacionado ao tipo de comunicaçõa que vc quer, no nosso caso é modbus serial, antes carregue esse sketch abaixo no seu arduino pegue um led e coloque na porta 3 do arduino a porta digital, dando tudo certo vc vai lá no watch list e marca 1 ou zero o o seu led vai acender e apagar via modbus serial é bem legal do resto é só configurar outros sensores que deve funcionar,

/*         
 Modbus serial - RTU Slave Arduino Sketch
 
 Marcos Daniel Wiechert wiechertdaniel@yahoo.com.br
 
 Baseado na biblioteca de Juan Pablo Zometa : jpmzometa@gmail.com
 http://sites.google.com/site/jpmzometa/
 and Samuel Marco: sammarcoarmengol@gmail.com
 and Andras Tucsni.
 
 As funções do protocolo MODBUS implementadas neste código:
  3 - Read holding registers;
  6 - Preset single register;
 16 - Preset multiple registers.
 
 This program is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2 of the License, or
 (at your option) any later version.
 
 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software
 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 
 The functions included here have been derived from the
 Modicon Modbus Protocol Reference Guide
 which can be obtained from Schneider at www.schneiderautomation.com.
 
 This code has its origins with
 paul@pmcrae.freeserve.co.uk (http://www.pmcrae.freeserve.co.uk)
 who wrote a small program to read 100 registers from a modbus slave.

 */


/*
 * configure_mb_slave(baud, parity, tx_en_pin)
 *
 * configuração dos parametros da porta serial.
 *
 * baud: taxa de transmissão em bps (valores típicos entre 9600, 19200... 115200)
 * parity: seta o modo de paridade:
 *         'n' sem paridade (8N1); 'e' paridede impar (8E1), 'o' paridade par (8O1).
 * tx_en_pin: pino do arduino que controla a transmissão/recepção em uma linha RS485.
 *        0 or 1 desliga esta função (para rede RS232)
 *        >2 para uma rede multiponto.
 */
void configure_mb_slave(long baud, char parity, char txenpin);

/*
 * 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
 *
 * slave: endereço do escravo (arduino) (1 to 127)
 * regs: uma matriz com os holding registers. Eles começam no endereço 1 (mestre ponto de vista)
 * Regs_size: número total de holding registers.
 * Retorna: 0 se não houver pedido do mestre,
 * NO_REPLY (-1) se nenhuma resposta é enviada para o mestre
 * Caso um código de exceção (1 a 4) em algumas exceções de modbus
 * O número de bytes enviados como resposta (> 4) se OK.
 */

int update_mb_slave(unsigned char slave, int *regs,
unsigned int regs_size);

/* Aqui começa o código do exemplo */

/* Parâmetros Modbus RTU de comunicação, o Mestre e os escravos devem usar os mesmos parâmetros */
enum {
        COMM_BPS = 9600, /* baud rate */
        MB_SLAVE = 1,     /* endereço do escravo modbus */
        PARITY = 'n'     /* paridade */
};

/* 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_3,     /* Controle do Led no pino  3 (desliga=0 liga=1) */

        MB_REGS           /* número total de registros do escravo */
};

int regs[MB_REGS];
int ledPin3  =  3;


void setup()
{
        /* configura cominicação modbus
         * 9600 bps, 8N1, RS485 network */
        configure_mb_slave(COMM_BPS, PARITY, 2);
        
        pinMode(ledPin3,  OUTPUT);
        
}


void loop()
{
        /* verifica se há solicitações do mestre */
        update_mb_slave(MB_SLAVE, regs, MB_REGS);
               
                
         
        

        /* os valores dos registros são definidos pelo mestre modbus (SCADA) */
        
                switch(regs[MB_PINO_3]) {
        case 1:
                digitalWrite(ledPin3, HIGH);
                break;        
        default: /* apagado */
                digitalWrite(ledPin3, LOW);
        }
        
                   
}

/****************************************************************************
 * INICIO DAS FUNÇÕES ESCRAVO Modbus RTU
 ****************************************************************************/

/* variaveis globais */
unsigned int Txenpin = 2;        /*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,   //Read contiguous block of holding register (Ler um bloco contíguo de registos)
        FC_WRITE_REG  = 0x06,   //Write single holding register (Escrever em um único registro)
        FC_WRITE_REGS = 0x10    //Write block of contiguous registers (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.print(query[i], BYTE);
        }

        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, sem exceção */
}



/************************************************************************
 *
 *     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++) {
                /* mudar reg hi_byte para temp */
                temp = (int) query[(BYTE_CNT + 1) + i * 2] 8;
                /* OR com 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;                                
        }
}

logo que resolver essa pendenga de RS485  estarei mais focado no protocolo modbus a assim vou poder ajudar mais.

Adriano muito obrigado, consegui fazer funcionar, muito legal...

você saber me informar quantos arduinos eu posso conectar no barramento RS485?

e será possível conectar outros dispositivos, tais com inversores, sofstarter,  e outros tipos de controladores com  CLP na mesma rede.  

AhAAAA!!


Isso eu posso falar um pouquinho, Kleber essa comunicação usb que vc fez é bom pra  de repente vc apresentar um projetinho de escola, mas o RS485  da pra fazer muita coisa.

com um conversor usb/rs485 e mais uma plaquiha TTl/RS485, sendo que a plaquinha se relaciona com cada arduino, e o codigo modbus vc determina o numeros de escravos da rede, tambem dependenddo da distancia vc pode ter até 10MB/S(para uma distancia de 10 a 50m) de transmissão se nao estou enganado, e colocar até 32 escravos na rede,a rede pode ir até 1200m, mas o seu Enio ja colocou até mais distante, ele montou uma rede RS485  que funciona tranquilamente desde 2001 até hoje em RS485, o RS485 e o modbus são padroes industriais bastante utilizado no mercado, então tudo isso que vc falou o arduino-modbus-RS485-ScadaBR(o quarteto fantastico) faz com uma mao nas costas, tanto que utilizaremos este quarteto pra montar uma rede totalmente integrada no sitio do sidney . Esse post do scadaBr não vai parar só no quarteto mais vai ainda ter muito mais coisas pois estamos dedicados de corpo e alma a este trabalho, assim que dominarmos este quarteto passaremos as integrações dos dispositivos e desenvolveremos tecnlogia (hardware e software) de acordo com as necessidades que tivermos e dos nosso colegas que postam aqui no scadaBR, suas duvidas, e assim caminhos juntos

Olá Kleber,

Apenas complementando o que o Adriano escreveu, um barramento RS485 pode conter até 32 dispositivos por segmento, mas esse número pode ser extendido com o uso de repetidores na rede. Os repetidores também podem servir para os casos onde a distância é muito grande ou quando seja necssãrio aumentar a taxa de transmissão.

E é possível incluir qualquer um dos dispositivos citados por você ( CLP, inversor, soft starter, multimedidores, etc) apenas que todos devem ter capacidade de comunicação pelo protocolo MODBUS RTU, estar com as mesmas configurações de porta serial (baud rate, start e stop bits, paridade) e cada dispositivo deve ter um endereçõ MODBUS distinto na rede.

A rede RS485 é realmente uma das redes mais usadas na indústria e automação, seja pela simplicidade, robustez, custo baixo e ser multiponto. Mas as vezes não é a única opção a escolha.

Não se engane, existem muitas aplicações sérias para uma rede RS232 ou USB que vão muito além de simples apresentações acadêmicas, tudo depende da aplicação, por exemplo, se o projeto tem uma topologia onde exista apenas um único escravo que esteja a poucos metros do mestre é possível usar USB ou RS232. Outro uso seguro para o USB é durante a fase de configuração e depuração do projeto para testar os bugs da aplicação sem termos que nos preocupar em saber se a rede física esta OK e focar no software.

É isso ai.

Ai pessoal o vídeo ficou com a qualidade baixa, tremidoooooo, mas da para visualizar a ideia do supervisório.
obrigado a todos que ajudaram. 

Parabéns, Kleber.

É show de bola o seu supervisório, pricipalmente que ele já se encontra no barramento RS485, eu ainda to na pendenga de conseguir colocar no barramento RS485, pois me enrrolei demais, mas vai sair também, vou te passar mais algumas informações sobre o RS485 mais tarde, mas te falo que sua imaginação é o limite do que vc pode fazer com o que vc ja tem pronto.
Espero ver muitos mais trabalhos com o scadabr.

abraços

olá povo,

depois de montar o TTl/RS485 na protoboard,liguei tudo e ativei no escadabr, o RS485 apareceu, e detalhe logo que liguei tava tudo apagado e tranquilo e depois criei os datasource e o datapoint, os dois leds ficam piscando freneticamene, eu usei o seguinte codigo:

/*         
 Modbus serial - RTU Slave Arduino Sketch
 
 Marcos Daniel Wiechert wiechertdaniel@yahoo.com.br
 
 Baseado na biblioteca de Juan Pablo Zometa : jpmzometa@gmail.com
 http://sites.google.com/site/jpmzometa/
 and Samuel Marco: sammarcoarmengol@gmail.com
 and Andras Tucsni.
 
 As funções do protocolo MODBUS implementadas neste código:
  3 - Read holding registers;
  6 - Preset single register;
 16 - Preset multiple registers.
 
 This program is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2 of the License, or
 (at your option) any later version.
 
 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software
 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 
 The functions included here have been derived from the
 Modicon Modbus Protocol Reference Guide
 which can be obtained from Schneider at www.schneiderautomation.com.
 
 This code has its origins with
 paul@pmcrae.freeserve.co.uk (http://www.pmcrae.freeserve.co.uk)
 who wrote a small program to read 100 registers from a modbus slave.

 */


/*
 * configure_mb_slave(baud, parity, tx_en_pin)
 *
 * configuração dos parametros da porta serial.
 *
 * baud: taxa de transmissão em bps (valores típicos entre 9600, 19200... 115200)
 * parity: seta o modo de paridade:
 *         'n' sem paridade (8N1); 'e' paridede impar (8E1), 'o' paridade par (8O1).
 * tx_en_pin: pino do arduino que controla a transmissão/recepção em uma linha RS485.
 *        0 or 1 desliga esta função (para rede RS232)
 *        >2 para uma rede multiponto.
 */
void configure_mb_slave(long baud, char parity, char txenpin);

/*
 * 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
 *
 * slave: endereço do escravo (arduino) (1 to 127)
 * regs: uma matriz com os holding registers. Eles começam no endereço 1 (mestre ponto de vista)
 * Regs_size: número total de holding registers.
 * Retorna: 0 se não houver pedido do mestre,
 * NO_REPLY (-1) se nenhuma resposta é enviada para o mestre
 * Caso um código de exceção (1 a 4) em algumas exceções de modbus
 * O número de bytes enviados como resposta (> 4) se OK.
 */

int update_mb_slave(unsigned char slave, int *regs,
unsigned int regs_size);

/* Aqui começa o código do exemplo */

/* Parâmetros Modbus RTU de comunicação, o Mestre e os escravos devem usar os mesmos parâmetros */
enum {
        COMM_BPS = 9600, /* baud rate */
        MB_SLAVE = 1,     /* endereço do escravo modbus */
        PARITY = 'n'     /* paridade */
};

/* 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_3,     /* Controle do Led no pino  3 (desliga=0 liga=1) */

        MB_REGS           /* número total de registros do escravo */
};

int regs[MB_REGS];
int ledPin3  =  3;


void setup()
{
        /* configura cominicação modbus
         * 9600 bps, 8N1, RS485 network */
        configure_mb_slave(COMM_BPS, PARITY, 2);
        
        pinMode(ledPin3,  OUTPUT);
        
}


void loop()
{
        /* verifica se há solicitações do mestre */
        update_mb_slave(MB_SLAVE, regs, MB_REGS);
               
                
         
        

        /* os valores dos registros são definidos pelo mestre modbus (SCADA) */
        
                switch(regs[MB_PINO_3]) {
        case 1:
                digitalWrite(ledPin3, HIGH);
                break;        
        default: /* apagado */
                digitalWrite(ledPin3, LOW);
        }
        
                   
}

/****************************************************************************
 * INICIO DAS FUNÇÕES ESCRAVO Modbus RTU
 ****************************************************************************/

/* variaveis globais */
unsigned int Txenpin = 2;        /*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,   //Read contiguous block of holding register (Ler um bloco contíguo de registos)
        FC_WRITE_REG  = 0x06,   //Write single holding register (Escrever em um único registro)
        FC_WRITE_REGS = 0x10    //Write block of contiguous registers (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.print(query[i], BYTE);
        }

        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, sem exceção */
}



/************************************************************************
 *
 *     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++) {
                /* mudar reg hi_byte para temp */
                temp = (int) query[(BYTE_CNT + 1) + i * 2] 8;
                /* OR com 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;                                
        }
}

é este codigo que estou usando no arduino, me parece que a rede ta funcionando, afinal na pesquisa de nó aparece 1 nó, a ideia era acender um led e apagalo pelo scadabr mas mesmo detectando a rede não consegui seta o led

agora deletei o datasource e os leds tanto de tx quanto de rx pararam de piscar, to encucado???

me parece que o barramento RS485 está ok, talvez seja alguma configuração do scadabr ou o sketch que eu usei ta bugado????

Gente!!!

problema resolvido, a questão era apenas que eu tinha que setar lá no datasource um campo chamado concurrency para slave, depois só fiz o BABA de sempre e o meu led setavel acendeu e apagou direitinho,SIDNEEEEEEEEEEEEEY, TA DORMINDO!!! agora são 3:19 da manha, daqui a pouco vou pro batente, mas fechei essa budega
,

RSS

© 2024   Criado por Marcelo Rodrigues.   Ativado por

Badges  |  Relatar um incidente  |  Termos de serviço