Colocamos aqui todos os tutoriais onde foram utilizados shields para Arduino ou Garagino.
Arduino
Tutorial: Controlando a potência dissipada de uma lâmpada com Dimmer Shield e Módulo Bluetooth
Aviso: Caros Garagistas, a Incubadora do Lab de Garagem não está recebendo novos projetos nesse momento. Avisaremos quando a chamada para novos projetos for reaberta. Obrigado!
Sua ideia pode se tor
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 |= ((1<<UPM01) | (1<<UCSZ01) | (1<<UCSZ00)); // UCSR0C &= ~((1<<UPM00) | (1<<UCSZ02) | (1<<USBS0)); break; case 'o': // 8O1 UCSR0C |= ((1<<UPM01) | (1<<UPM00) | (1<<UCSZ01) | (1<<UCSZ00)); // UCSR0C &= ~((1<<UCSZ02) | (1<<USBS0)); break; case 'n': // 8N1 UCSR0C |= ((1<<UCSZ01) | (1<<UCSZ00)); // UCSR0C &= ~((1<<UPM01) | (1<<UPM00) | (1<<UCSZ02) | (1<<USBS0)); 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; }}…
tei), existem duas tensões: a estática e a dinâmica (ou instantânea). Vou explicar melhor. Sabemos que existe um consumo "médio" de energia no Remoto, o que implica que existe também uma corrente média. Sabemos que a tensão entre a linha "laranja" e a "marrom", não será 5V, pois os fios do cabo CAT que levam a alimentação, tem uma Resistência Elétrica (e na verdade existe também uma capacitância e uma indutância "distribuídas" ao longo do fio). Então de fato, todo o circuito do remoto está em série com esta Resistência equivalente do Cabo, e assim temos um Divisor de Tensão. Considere que em determinado momento, o consumo de energia do Remoto está fixo, ou seja, não muda e seria "X Watts". Nesta condição, o circuito do Remoto terá uma Resistência elétrica equivalente, como se fosse um Resistor. Assim, se vc pegar essa Resistência e a Resistência equivalente do Cabo, que estão em série, e aplicar 5V, podemos calcular o Divisor de Tensão, e obteremos facilmente qual a tensão resultante sobre cada uma das Resistências. Mas o que nos interessa é a tensão sobre o Remoto, já que para que este funcione de forma estável, deve ser a mais próxima possível de 5V, o que automaticamente implica que desejamos que a Resistência equivalente do cabo, seja a menor possível. Nesta condição, em que o consumo de energia do Remoto está fixo, se tivermos um Capacitor de 1uF entre as linhas "laranja" e "marrom", terá o mesmo resultado que se tivermos um Capacitor de 1000uF, ou seja: a tensão resultante no Remoto dependerá exclusivamente do Divisor de Tensão que calculamos com as Resistências, e o valor do Capacitor não terá influência neste valor (na prática isto não é 100% verdadeiro, pois os Capacitores não são perfeitos e possuem corrente de fuga, ou seja, possuem alguma Resistência elétrica equivalente, embora bastante alta). Nesta análise, estamos considerando que "pulamos" o momento em que o circuito é ligado pela primeira vez, ou seja: já consideramos que passou bastante tempo (por exemplo vários minutos) desde o momento que o circuito foi ligado.
Mas sabemos que em algum momento, o consumo de energia do Remoto irá mudar, podendo aumentar ou diminuir em relação ao valor que consideramos como "estático" na análise inicial. Quando isso ocorrer pense da seguinte forma: o consumo vai mudar, mais depois de algum tempo, ele será o novo "consumo" estático, e vc poderá novamente calcular o Divisor de tensão para esta nova condição. Como a Resistência do Cabo não mudou e ainda é muito mais baixa que a Resistência equivalente do Remoto, ao calcularmos o Divisor de Tensão para esta nova condição de consumo, a tensão resultante sobre o Remoto, terá uma diferença pequena em relação à condição inicial de análise. Outra conclusão importante, é que quando o consumo de energia do Remoto aumentar, sua resistência elétrica equivalente irá diminuir em relação à primeira análise, e ao calcularmos o Divisor de tensão, teremos por consequência que a tensão sobre o Remoto será menor em relação à análise inicial (ou seja: se distancia mais do 5V). Da mesma forma, se o consumo de energia diminuir, a resistência do Remoto irá aumentar, e portanto ao calcularmos a tensão, teremos que a mesma irá aumentar em relação à análise inicial (ou seja: se aproxima mais do 5V).
Tendo estes comportamentos ocorrendo no consumo de Energia do Remoto, como a coisa se comportará com um Capacitor ligado em paralelo com a alimentação?
Simples: os Capacitores "reagem" à "tentativa" de mudança da tensão entre seus terminais (por isso que dizemos "reatância" capacitiva, devido à esta reação). Mas eles podem fazer isso por algum tempo apenas (pois tem uma "capacidade" limitada), e o que eles conseguem é apenas retardar a mudança, fazendo com que ela seja mais lenta do que seria se o Capacitor não estivesse ali. Ao final das contas, a tensão sempre irá para um valor "estático" que pode ser calculado por Divisor de Tensão. Quanto maior o valor do Capacitor, mais ele conseguirá retardar essa mudança na tensão. Ou seja: ele acaba funcionando como um Filtro "passa-baixas", mas não vai impedir que a tensão efetivamente mude, apenas vai retardar essa mudança, tornando-a mais "suave".
Então a resposta é: o Capacitor Eletrolítico de 1000uF no circuito, é usado como Filtro, e filtro aqui significa: suavizar as mudanças da tensão.
Mas veja: tem algo muito importante que vc precisa visualizar sobre essa filtragem. Lembra-se que eu disse que não precisava colocar um Capacitor Cerâmico em paralelo com o Eletrolítico? E eu justifiquei dizendo que estes Capacitores já estavam nas plaquinhas dos módulos, e já com valores adequados. E de fato estão lá mesmo (olhe os circuitos destas plaquinhas e vai encontrá-los lá). Mas por que isso tudo? Por que tem que ter estes Capacitores Cerâmicos, e porque o melhor lugar pra eles é junto a cada módulo?
Respondendo: ocorre que os Eletrolíticos de Alumínio (como aquele de 1000uF no circuito), tem uma alta capacitância, e isto é ótimo para suavizar a tensão conforme vimos. Esta alta capacitância é conseguida devido ao tamanho "grande" desses componentes, mas também devido à forma como são construídos "enrolados" permitindo uma grande área nas "placas" capacitivas (semelhante a enrolar 3 tapetes juntos, mas um sobre o outro, como se fossem 3 camadas: condutor, isolante, condutor). Mas isso também tem um efeito colateral extremamente indesejado: os Eletrolíticos de Alumínio (dizemos assim porque o de Tântalo também é eletrolítico), tem uma indutância equivalente muito alta (como sabemos, condutores "enrolados" aumentam a indutância). Esta indutância é tão alta, que para tensões variando acima de 1 kHz, estes Capacitores passam a ser praticamente só Indutores, ou seja: passam a ser comportar exatamente o contrário do que se esperava deles.
Mas claro, é bem provável que entre os pontos onde um Capacitor está ligado, acabem aparecendo variações de tensão de uma ampla gama de frequências. Assim no caso do Capacitor de 1000uF, se tivermos variações de baixa frequência (ou seja variações lentas no consumo de energia), a alta capacitância dos Eletrolíticos será ótima para suavizar a intensidade destas variações.
Na natureza existe uma relação muito clara entre intensidade e duração. A maioria das energias que tem variação muito grande na sua amplitude, também tem uma duração mais curta. E quando a variação de amplitude é menor, temos maior duração nesta variação. Na verdade este é o próprio conceito de Potência, que é nada mais do que a capacidade de um Sistema de fornecer energia por unidade de tempo (exatamente como quando comparamos um corredor velocista dos 100m com um dos 400m).
Em um Sistema Elétrico, temos os diversos módulos sendo alimentados. Somando o consumo de energia de todos eles, teremos o valor "estático" que falamos antes. Mas pode ocorrer que apenas um desses módulos tenha uma variação da energia consumida em um determinado momento, e quando isso ocorrer, essa variação será apenas um pequeno percentual do total consumido pelo Sistema. Mas há também uma outra característica dessa variação: ela normalmente dura bem pouco tempo, ou seja é rápida, ou ainda: se comporta como uma alta-frequência. Então Capacitores Eletrolíticos seriam péssimos para "suavizar" essa variação. É aí que entram os Capacitores Cerâmicos. Eles tem baixo valor de capacitância, mas também não precisa muito, porque a intensidade da variação é apenas um pequeno percentual do total. Mas o que realmente faz a diferença, é que os Capacitores Cerâmicos tem uma Indutância baixíssima, o que faz com que consigam funcionar a frequências muito altas nos circuitos (o que o Eletrolítico jamais poderia conseguir).
Ou seja, os Capacitores Cerâmicos nos módulos, dão conta das variações de consumo consumo "instantâneas" naqueles módulos. E eles devem estar fisicamente próximos aos pontos onde a variação ocorre (enquanto o Eletrolítico estará bem mais distante, lá na entrada da alimentação "geral"). Há outro motivo muito importante para que eles estejam fisicamente próximos dos pontos onde ocorre a variação: diminuir o EMI (Interferência Eletro-Magnética), mas não vou abordar isso aqui pra não demorar mais na questão.
2) é simples se estimar o consumo de um determinado circuito. Não vou mostrar isso aqui neste momento, mas estimei o consumo do seu Remoto (incluindo os dois drivers RS485 do outro lado) e ele deve ficar algo próximo a 180mA, de forma "estática". Quando o Buzzer for acionado, irão ocorrer picos neste consumo, que provavelmente será próximo de 250mA. Mas o Buzzer é acionado durante muito pouco tempo, e portanto podemos desconsiderá-lo quando formos analisar se o "StepDown" irá "aguentar". Lembrando que estes valores são estimativas.
Como sabemos o consumo, no caso 180mA, podemos facilmente calcular a Potência que será: 5V x 180mA = 0.9W , ou seja, quase 1W. Essa Potência é bem abaixo daquela na qual é recomendada que se use um dissipador no "StepDown" (o CI LM2596). Conclusão, nenhum dissipador será necessário. Claro, como eu disse, existirão picos quando se aciona o Buzzer, mas como a duração é pequena, praticamente não terá efeito no "calor" dissipado no LM2596. E mesmo que vc considere estes picos, ainda assim a Potência será baixa para o LM2596.
Atenção: não use essa corrente, para calcular a dissipação de Potência do "StepDown" como se ele fosse um Regulador Linear, porque ele não é. Ele é um "conversor chaveado". Então, desconsiderando as perdas pelo fato do rendimento não ser 100%, a corrente que "entra" no LM2596 será a Potência que calculamos, dividida pela tensão que entra no conversor, ou seja, dividida por 12V (e quanto maior o rendimento ou eficiência do conversor, mais este valor se aproximará do real).
3) exatamente. A configuração que coloquei para os Drivers RS485, fazem com que eles sejam "drivers unidirecionais", e vc não precisa de um Processador para ficar chaveado a direção dos sinais. E também por este motivo vc precisa de dois drivers em cada lado, pois existem duas direções fixas: do TX do Sensor para o RX do Arduino, e do TX do Arduino para o RX do Sensor.
Claro, isso tem duas desvantagens: dobra a quantidade necessária de drivers RS485, e aumenta o consumo de energia destes drivers (pois eles nunca são "desligados"), sendo que cada driver consome cerca de 20mA. Mas a vantagem é que vc não precisa de um Arduino lá no Remoto, e assim não precisa de um código a mais e de um protocolo entre dois Arduinos (muita gente tem pavor disso, mas sabendo fazer da forma correta é simples, mas não deixa de ser chato).
E claro também: vc pode usar a mesma técnica sempre que precisar enviar sinais digitais de um ponto a outro, a distâncias que podem superar até 1km.
Abrçs,
Elcids…
Adicionado por Elcids Chagas ao 13:24 em 20 março 2020
corrente não passa pelos circuitos da placa Mãe, seja Notebook, seja Desktop).
Sobre a questão de ser problema do "Fuse PTC", também acho improvável, por dois motivos. Primeiro, é altamente "anormal" que um "Fuse PTC" seja danificado, embora isto não seja impossível. Segundo: vc relatou que o problema ocorre em todas as suas Portas USB, e assim teria que ter sido danificado o "Fuse PTC" individual de cada Porta, o que parece também improvável.
Mas aqui tem um ponto de atenção: lembra-se que eu disse não saber como meu amigo danificou o "Fuse PTC" na placa mãe do Notebook dele? Pois é, depois que eu fiz o diagnóstico e ele foi consertar aquele "Fuse PTC" específico (ele colocou um fio no lugar do "Fuse PTC", kkkkkk), ele também constatou que os "Fuse PTC" de outras duas Portas USB também estavam com o mesmo problema. Ou seja: ou o lote de fábrica do "Fuse PTC" usado no Notebook dele tinha um problema crônico de fabricação (o que pode ocorrer, embora num percentual extremamente pequeno, praticamente improvável), ou ele fez a mesma coisa "errada" em todas as Portas USB do Notebook e acabou danificando uma a uma (o que ele fez, eu não sei, pois nem ele se lembrava de alguma coisa que pudesse ter sido - pois ele conserta notebooks, então são tantas coisas que a pessoa faz no dia a dia, que pode ser difícil lembrar).
Outra coisa: não é o "correto", mas eventualmente poderia haver apenas um "Fuse PTC" único usado em comum para todas as Portas USB. Assim se este único desse problema, afetaria todas as Portas. Mas como eu disse, isto não é o "correto" em um Projeto, então acho improvável essa possibilidade. Mas é sempre bom lembrar.
Também pode ocorrer de na sua placa haver um "Gerenciador de Energia" para as Portas USB, o que ocorre em algumas Placas mais sofisticadas, e este gerenciador nada mais é do que um Chip que monitora (e eventualmente regula) a tensão fornecida nas Portas e também monitora a corrente drenada nas mesmas (pra avisar ao Sistema Operacional da Máquina, que uma determinada Porta está exagerando na demanda de corrente, e assim possibilitar que o "tal" Sistema Operacional tome alguma providência...). Assim, por ser um chip, não é impossível que tenha sofrido algum dano (por motivos diversos), e aí ele pode "enlouquecer", causando os mais diversos problemas, em uma única Porta ou em todas.
Sobre o "par-trançado" do "D+" e "D-", é simples: este par de sinais é "complementar", ou seja, forma um "par diferencial". Se vc já usou a Interface RS485 entenderá bem, pois nesta interface, aqueles dois sinais "A" e "B" são assim também. A técnica de "par-trançado" é usada para "cancelamento mútuo" de ruídos em uma linha de comunicação usando sinais que são complementares (o "par diferencial"). Esta técnica também diminui absurdamente a temida "EMI", e isto é tanto mais importante quando maior for a velocidade da comunicação. Além disso, se o "par-trançado" não for bom, pode ocorrer o problema do "skew" entre os dois sinais, e aí a comunicação não funciona de jeito algum. Uma Interface que também usa esta tecnologia, é o "SATA", usada na Interface com HDs nos computadores. Se vc já teve algum dia que comprar aqueles cabos SATA (eu já comprei muitos), já deve ter enfrentado a coisa, pois é muito comum esses cabos darem problemas, e isso pode ocorrer por não ter o "par-trançado" no cabo (por redução de custos de fabricação) ou mais comumente por não ser um bom "par-trançado" (pois a velocidade do SATA é muito alta, passando de 3 GHz, o que exige um ótimo "par-trançado" pra diminuir ao máximo a possibilidade de ocorrer "skew" entre os dois sinais).
Vc pode aprender um pouco mais sobre "sinais complementares", olhando um artigo sobre RS-485 neste link: "RS-485"
Naquele artigo, atente principalmente à figura 1 (link: "sinais diferenciais"), e à figura 4 (link: "sinais A e B").
E no caso do USB, desde sempre, o "D+" e o "D-" são sinais diferenciais com velocidades bastante altas, então um "par-trançado" é obrigatório. A não ser que vc queira ter problemas, como salientei antes.
Abrçs,
Elcids…
Adicionado por Elcids Chagas ao 21:37 em 16 fevereiro 2022
o ondde li que existe uma biblioteca que resume nossas skeths tirando a parte que começa na linha descrita como INICIO DAS FUNÇÕES ESCRAVO sem alterar o restante. Alguém sabe de onde posso baixar esta biblioteca? Ou alguém que saiba pode criar uma e disponibilizar aqui facilitando a vida de todos os garagistas que estão se matando como eu para escrever skeths tão longos como este ou melhor copiar e colar tudo isto sempre?
void configure_mb_slave(long baud, char parity, char txenpin);
int update_mb_slave(unsigned char slave, int *regs,unsigned int regs_size);
/**************************************************************************** Aqui começa o código do exemplo ********************************************************************************//******************************************************************************* EDITAR A PARTIR DAQUI ***************************************************************************************/
/* Parâmetros Modbus RTU de comunicação, o Mestre e os escravos devem usar os mesmos parâmetros */enum { COMM_BPS = 9600, /* baud rate */ PARITY = 'n', /* paridade */ MB_SLAVE = 1, /* endereço do escravo modbus */ TXENDPIN = 2 };
/* 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_A0, /* Leitura da entrada analógica 0 (0 a 1023) */ MB_A1, /* Leitura da entrada analógica 1 (0 a 1023) */ MB_A2, /* Leitura da entrada analógica 2 (0 a 1023) */ MB_A3, /* Leitura da entrada analógica 3 (0 a 1023) */ MB_A4, /* Leitura da entrada analógica 4 (0 a 1023) */ MB_A5, /* Leitura da entrada analógica 5 (0 a 1023) */
MB_PINO_3, /* Controle do Led no pino 3 (desliga=0 liga=1) */ MB_PINO_4, /* Controle do Led no pino 4 (desliga=0 liga=1) */ MB_PINO_5, /* Controle do Led no pino 5 (desliga=0 liga=1) */ MB_PINO_6, /* Controle do Led no pino 6 (desliga=0 liga=1) */ MB_PINO_7, /* Controle do Led no pino 7 (desliga=0 liga=1) */ MB_PINO_8, /* Controle do Led no pino 8 (desliga=0 liga=1) */ MB_PINO_9, /* Controle do Led no pino 9 (desliga=0 liga=1) */ MB_PINO_10, /* Controle do Led no pino 10 (desliga=0 liga=1) */ MB_PINO_11, /* Controle do Led no pino 11 (desliga=0 liga=1) */ MB_PINO_12, /* Controle do Led no pino 12 (desliga=0 liga=1) */ MB_PINO_13, /* Controle do Led no pino 13 (desliga=0 liga=1) */ MB_REGS /* número total de registros do escravo */};
int regs[MB_REGS];int ledPin3 = 3;int ledPin4 = 4;int ledPin5 = 5;int ledPin6 = 6;int ledPin7 = 7;int ledPin8 = 8;int ledPin9 = 9;int ledPin10 = 10;int ledPin11 = 11;int ledPin12 = 12;int ledPin13 = 13;
unsigned long wdog = 0; /* watchdog */unsigned long tprev = 0; /* tempo anterior do último comando*/unsigned long tanalogprev = 0; /* tempo anterior da leitura dos pinos analogicos*/
void setup() { /* configura cominicação modbus * 9600 bps, 8N1, RS485 network */ configure_mb_slave(COMM_BPS, PARITY, TXENDPIN); pinMode(ledPin3, OUTPUT); pinMode(ledPin4, OUTPUT); pinMode(ledPin5, OUTPUT); pinMode(ledPin6, OUTPUT); pinMode(ledPin7, OUTPUT); pinMode(ledPin8, OUTPUT); pinMode(ledPin9, OUTPUT); pinMode(ledPin10, OUTPUT); pinMode(ledPin11, OUTPUT); pinMode(ledPin12, OUTPUT); pinMode(ledPin13, OUTPUT); }
void loop() { /* verifica se há solicitações do mestre */ if(update_mb_slave(MB_SLAVE, regs, MB_REGS)) wdog = millis(); if ((millis() - wdog) > 10000) { /* desliga as saidas se não recebe comando a mais de 10 segundos */ regs[MB_PINO_3] = 0; /* desliga led 3 */ regs[MB_PINO_4] = 0; /* desliga led 4 */ regs[MB_PINO_5] = 0; /* desliga led 5 */ regs[MB_PINO_6] = 0; /* desliga led 6 */ regs[MB_PINO_7] = 0; /* desliga led 7 */ regs[MB_PINO_8] = 0; /* desliga led 8 */ regs[MB_PINO_9] = 0; /* desliga led 9 */ regs[MB_PINO_10] = 0; /* desliga led 10 */ regs[MB_PINO_11] = 0; /* desliga led 11 */ regs[MB_PINO_12] = 0; /* desliga led 12 */ regs[MB_PINO_13] = 0; /* desliga led 13 */ } if ((millis() - tanalogprev) > 1000) { /* atualiza as entradas analogica a cada 1 segundo */ regs[MB_A0] = analogRead(0); /* ler entrada analógica 0 */ regs[MB_A1] = analogRead(1); /* ler entrada analógica 1 */ regs[MB_A2] = analogRead(2); /* ler entrada analógica 2 */ regs[MB_A3] = analogRead(3); /* ler entrada analógica 3 */ regs[MB_A4] = analogRead(4); /* ler entrada analógica 4 */ regs[MB_A5] = analogRead(5); /* ler entrada analógica 5 */ tanalogprev = millis(); }
/* 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); } switch(regs[MB_PINO_4]) { case 1: digitalWrite(ledPin4, HIGH); break; default: /* apagado */ digitalWrite(ledPin4, LOW); }
switch(regs[MB_PINO_5]) { case 1: digitalWrite(ledPin5, HIGH); break; default: /* apagado */ digitalWrite(ledPin5, LOW); }
switch(regs[MB_PINO_6]) { case 1: digitalWrite(ledPin6, HIGH); break; default: /* apagado */ digitalWrite(ledPin6, LOW); }
switch(regs[MB_PINO_7]) { case 1: digitalWrite(ledPin7, HIGH); break; default: /* apagado */ digitalWrite(ledPin7, LOW); }
switch(regs[MB_PINO_8]) { case 1: digitalWrite(ledPin8, HIGH); break; default: /* apagado */ digitalWrite(ledPin8, LOW); }
switch(regs[MB_PINO_9]) { case 1: digitalWrite(ledPin9, HIGH); break; default: /* apagado */ digitalWrite(ledPin9, LOW); }
switch(regs[MB_PINO_10]) { case 1: digitalWrite(ledPin10, HIGH); break; default: /* apagado */ digitalWrite(ledPin10, LOW); }
switch(regs[MB_PINO_11]) { case 1: digitalWrite(ledPin11, HIGH); break; default: /* apagado */ digitalWrite(ledPin11, LOW); }
switch(regs[MB_PINO_12]) { case 1: digitalWrite(ledPin12, HIGH); break; default: /* apagado */ digitalWrite(ledPin12, LOW); }
switch(regs[MB_PINO_13]) { case 1: digitalWrite(ledPin13, HIGH); break; default: /* apagado */ digitalWrite(ledPin13, LOW); }
} /********************************************************************************** SÓ EDITAR ATÉ AQUI ****************************************************************************************//*************************************************************************** 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.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, 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 |= ((1<<UPM01) | (1<<UCSZ01) | (1<<UCSZ00)); // UCSR0C &= ~((1<<UPM00) | (1<<UCSZ02) | (1<<USBS0)); break; case 'o': // 8O1 UCSR0C |= ((1<<UPM01) | (1<<UPM00) | (1<<UCSZ01) | (1<<UCSZ00)); // UCSR0C &= ~((1<<UCSZ02) | (1<<USBS0)); break; case 'n': // 8N1 UCSR0C |= ((1<<UCSZ01) | (1<<UCSZ00)); // UCSR0C &= ~((1<<UPM01) | (1<<UPM00) | (1<<UCSZ02) | (1<<USBS0)); 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; }}…
Adicionado por Ederson Luiz ao 10:10 em 4 abril 2013