oop ( serial monitor) e se iniciam corretamente. Não sou programador, e tenho aprendido por aqui mesmo. (Obs: algumas bibliotecas se referem a módulos chineses alternativos que tenho e são um pouco diferente dos demais módulos - DHT11, COMPASS HMC5883 ). Ainda não comprei o motor com pás para medir a velocidade do vento, e o sensor de chuva. Mas já deixo instalado o sensor sônico para o pluviômetro, pode ser usado com cartão SD( o meus queimei por descuido, mas estava nesta programação, retirei) e o conjunto ficará em um balão amarado, que subirá aos poucos, poderá girar em seu eixo e inclinar conforme o vento. Também poderá ser usado em drones, por quem quiser usar o projeto.
Por favor vejam que o foco inicial é saber como testar no setup() se eles estão ok.
Atenção: Qualquer um que usar balões deve ter autorização de manejo de acordo com o estado e instituição de ensino, não deve realizar qualquer teste em condições de céu nublado ou chuva sem tomar orientação sobre os riscos, não me responsabilizo pelas tentativas de teste que causem risco de morte e contravenção da lei dos balões RJ.
// Marcelo da Silva Santos (Designer, esp. animador, estudante de engenharia mecanica)// Estacao meteriologica feita em casa / HOME MADE Meteriologoc station prototype// Portugues - Brasil - BR_pt (Lingua).// Rio de Janeiro 22:09 / Sexta-feira / 09 / Agosto / 2019.// 1.0v Dados livres. Obg a todos / FreeData. Tks Everybody!// Rascunho na memoria do uno / UNO Sketch memory 88%...!!! :(
#include <Ultrasonic.h>#define pino_trigger A0#define pino_echo A1Ultrasonic ultrasonic(pino_trigger, pino_echo);float cmMsec, inMsec;long microsec = ultrasonic.timing();//----------------------------------------------------------------------------------#include <dht.h>#define pinoDHT11 9dht sensorDHT11;//----------------------------------------------------------------------------------#include <Adafruit_Sensor.h> #include <Adafruit_BMP280.h> float PressaoDoDia;int Atm = 0; Adafruit_BMP280 bmp; //----------------------------------------------------------------------------------
#include <Wire.h>#include <MechaQMC5883.h>MechaQMC5883 qmc; int x, y, z; int azimuth;
//----------------------------------------------------------------------------------//#include <SD.h>
//Sd2Card SDcard;//SdVolume volume;
//const int chipSelect = 10; //----------------------------------------------------------------------------------#include <DS3232RTC.h>
const int MPU=0x68; int AcX,AcY,AcZ,Tmp,GyX,GyY,GyZ;
long ContadorLoop = 0; // CONTADOR DE LOOP(Tipo de Timer primitivo).int Ciclos =0; // contador de ciclos cheios do contador de loop.//----------------------------------------------------------------------------------
void setup(){ Serial.begin(9600); Wire.begin(); setSyncProvider(RTC.get); Wire.beginTransmission(MPU); Wire.write(0x6B); Wire.write(0); Wire.endTransmission(true); cmMsec = ultrasonic.convert(microsec, Ultrasonic::CM); inMsec = ultrasonic.convert(microsec, Ultrasonic::IN); if(!Serial) // 1 { Serial.println("Erro ao iniciar Serail! Verifique as conexões."); Serial.println("Verifique o Modulo, conexoes e reinicie o Arduino..."); Serial.print("Erro no Loop: "); Serial.println(ContadorLoop); } else { Serial.println("Serial iniciado!"); Serial.print("Inicio no Loop: "); Serial.println(ContadorLoop); } if(!bmp.begin(0x76)) // 2 { Serial.println("Sensor BMP280 não foi identificado! Verifique as conexões."); Serial.println("Verifique o Modulo, conexoes e reinicie o Arduino..."); Serial.print("Erro no Loop: "); Serial.println(ContadorLoop); } else { Serial.println("Sensor BMP280 foi identificado!");Serial.print("Inicio no Loop: "); Serial.println(ContadorLoop); } if(timeStatus() != timeSet) // 3 { Serial.println("Impossivel sincronismo com RTC"); Serial.println("Verifique o Modulo, conexoes e reinicie o Arduino..."); Serial.print("Erro no Loop: "); Serial.println(ContadorLoop); } else { Serial.println("RTC Esta definindo o tempo do sistema"); Serial.println("Módulo RTC foi identificado!"); Serial.print("Inicio no Loop: "); Serial.println(ContadorLoop); } if(!MPU, 0x6B) // 4 { Serial.println("Impossivel sincronismo com MPU6050"); Serial.println("Verifique o Modulo, conexoes e reinicie o Arduino..."); Serial.print("Erro no Loop: "); Serial.println(ContadorLoop); } else { Serial.println("Sincronismo com MPU6050, ok!"); Serial.print("Inicio no Loop: "); Serial.println(ContadorLoop); } if(inMsec=0) // 5 { Serial.println("Nenhum pulso detectado de Ultrasonico!"); Serial.println("Esperando para teste, sincronismo com Ultrasonic"); delay(2500); Serial.println("Novo teste iniciado!"); cmMsec = ultrasonic.convert(microsec, Ultrasonic::CM); inMsec = ultrasonic.convert(microsec, Ultrasonic::IN); if((inMsec=0) && (cmMsec=0 )) { Serial.println("Impossivel sincronismo com Ultrasonic"); Serial.println("Verifique o Modulo, conexoes e reinicie o Arduino..."); Serial.print("Erro no Loop: "); Serial.println(ContadorLoop); } else { Serial.println("Detectado sincronismo com Ultrasonic no segundo teste!"); } } else { Serial.println("Sincronismo com Ultrasonic, ok!"); Serial.print("Inicio no Loop: "); Serial.println(ContadorLoop); } sensorDHT11.read11(pinoDHT11); if((sensorDHT11.humidity=0) && ((sensorDHT11.temperature)=0)) // 6 { sensorDHT11.read11(pinoDHT11); delay(2500); if((sensorDHT11.humidity=0) && ((sensorDHT11.temperature)=0)) { Serial.println("Impossivel sincronismo com DHT_11"); Serial.println("Verifique o Modulo, conexoes e reinicie o Arduino..."); Serial.print("Erro no Loop: "); Serial.println(ContadorLoop); } else { Serial.println("Sincronismo com DHT_11, ok!"); Serial.print("Inicio no Loop: "); Serial.println(ContadorLoop); } } else { Serial.println("Sincronismo com DHT_11, ok!"); Serial.print("Inicio no Loop: "); Serial.println(ContadorLoop); } if((x=-1) && ( y=-1)) // 7 { x=0; y=0; z=0; delay(2500); if((x=-1) && (y=-1)) { Serial.println("Impossivel sincronismo com Bussola!"); Serial.println("Verifique o Modulo, conexoes e reinicie o Arduino..."); Serial.print("Erro no Loop: "); Serial.println(ContadorLoop); } else { Serial.println("Sincronismo com a Busola na segundo teste, ok!"); Serial.print("Inicio no Loop: "); Serial.println(ContadorLoop); } } else { Serial.println("Sincronismo com a bussola, ok!"); Serial.print("Inicio no Loop: "); Serial.println(ContadorLoop); }}
void loop(){ Serial.println("----------------------------------------------------------------------------------"); // ________________________ RTC ___________________________________________________________________________________________________ digitalClockDisplay(); // _______________________ DHT 11 _________________________________________________________________________________________________ sensorDHT11.read11(pinoDHT11); // _______________________ BMP 280 ________________________________________________________________________________________________ PressaoDoDia = 1013,25; // pressao ao nivel do mar, // ajustar este valor pela internet no mesmo dia. // _______________________ BUSSOLA ________________________________________________________________________________________________ qmc.read(&x, &y, &z,&azimuth); // _______________________ MPU 6050 _______________________________________________________________________________________________ Wire.beginTransmission(MPU); Wire.write(0x3B); Wire.endTransmission(false); Wire.requestFrom(MPU,14,true); AcX=Wire.read()8|Wire.read(); //0x3B (ACCEL_XOUT_H) & 0x3C (ACCEL_XOUT_L) AcY=Wire.read()8|Wire.read(); //0x3D (ACCEL_YOUT_H) & 0x3E (ACCEL_YOUT_L) AcZ=Wire.read()8|Wire.read(); //0x3F (ACCEL_ZOUT_H) & 0x40 (ACCEL_ZOUT_L) Tmp=Wire.read()8|Wire.read(); //0x41 (TEMP_OUT_H) & 0x42 (TEMP_OUT_L) GyX=Wire.read()8|Wire.read(); //0x43 (GYRO_XOUT_H) & 0x44 (GYRO_XOUT_L) GyY=Wire.read()8|Wire.read(); //0x45 (GYRO_YOUT_H) & 0x46 (GYRO_YOUT_L) GyZ=Wire.read()8|Wire.read(); //0x47 (GYRO_ZOUT_H) & 0x48 (GYRO_ZOUT_L) // _____________________________ Escrevendo no Serial _____________________________________________________________________________ if (ContadorLoop>0) { Serial.println("Iniciando Sistema, preparando CAIXA PRETA!"); Serial.print("INICIO DE Loop: "); Serial.println(ContadorLoop); Serial.println("-----------------------------Ultrasonic--------------------------------------------"); Serial.print("Distancia em cm: "); Serial.print(cmMsec); Serial.print(" - Distancia em polegadas: "); Serial.println(inMsec); Serial.println("-----------------------------DHT11------------------------------------------------"); Serial.print("Umidade: "); Serial.print(sensorDHT11.humidity); Serial.print("%"); Serial.print(" - Temperatura_1: "); Serial.print(sensorDHT11.temperature,0); Serial.println("C"); Serial.println("----------------------------BMP 280-----------------------------------------------"); Serial.print("Temperatura_2: "); Serial.print(bmp.readTemperature()); Serial.print(" *C (Grau Celsius)"); Serial.print(" - Pressao: "); Serial.print(bmp.readPressure()); Serial.print(" Pa (Pascal)"); Serial.print(" - Atmosfera: "); Atm = (((bmp.readPressure())/PressaoDoDia),1); Serial.println(Atm); Serial.println("-----------------------------Bussola----------------------------------------------"); Serial.print("x: "); Serial.print(x); Serial.print(" - y: "); Serial.print(y); Serial.print(" - z: "); Serial.print(z); Serial.print(" ROTACAO EM RELACAO AO NORTE: "); Serial.print(azimuth); Serial.println(); Serial.println("-----------------------------MPU 6050---------------------------------------------"); Serial.print("AcX = "); Serial.print(AcX); Serial.print(" | AcY = "); Serial.print(AcY); Serial.print(" | AcZ = "); Serial.print(AcZ); Serial.print(" | Tmp = "); Serial.print(Tmp/340.00+36.53); Serial.print(" | GyX = "); Serial.print(GyX); Serial.print(" | GyY = "); Serial.print(GyY); Serial.print(" | GyZ = "); Serial.println(GyZ); Serial.println("----------------------------------------------------------------------------------"); } if (ContadorLoop >=2147483646) { ContadorLoop=0; Ciclos=Ciclos+1; Serial.println(ContadorLoop); Serial.println(Ciclos); } Serial.print("FIM DE Loop: "); Serial.println(ContadorLoop); Serial.println("----------------------------------------------------------------------------------"); Serial.println(" "); Serial.println(" "); ContadorLoop = ContadorLoop + 1; } // _____________________________ Escrevendo no Serial Fim _________________________________________________________________________
void digitalClockDisplay(){ Serial.print(hour()); printDigits(minute()); printDigits(second()); Serial.print(' '); Serial.print(day()); Serial.print(' '); Serial.print(month()); Serial.print(' '); Serial.print(year()); Serial.println();}
void printDigits(int digits){ Serial.print(':'); if(digits < 10) { Serial.print('0'); Serial.print(digits); }}…
CD onde pretendo ter na primeira tela após ser acionado por um botão "bverde" uma mensagem com o nome da máquina passando então 1 segundo irá mostrar a quantidade a ser envasada. A quantidade será o valor digitado no Keypad;
- Após o valor ser digitado pelo keypad uma segunda tela é mostrada, nessa tela será contada a quantidade de envasada e pressionando o "bamarelo" muda a tela para a terceira tela onde irá mostrar o nível do tanque de armazenamento, pressionando o "bamarelo" novamente ele volta para a segunda tela, ou seja o "bamarelo" irá funcionar como um botão de opões que transita entre essas duas telas;
- Usando um decrementador quero que o mesmo decremente o valor digitado na tela 1 mostrando a mensagem de que o Lote esta pronto e que se deve inserir um novo valor;
- Um terceiro botão "bvermelho" seria o botão de emergência, ou seja se pressionado a máquina trava.
- Todo esse programa foi baseado em exemplos e projetos que encontrei aqui no fórum e em outros sites.
Segue o Programa:
//========================================================================
#include <Keypad.h>#include <LiquidCrystal.h> LiquidCrystal lcd(22,23,24,25,26,27); // DECLARAÇÃO DOS PINOS DO LCD NO ARDUINO
// DEFINIÇÃO DOS BOTÕES - VERDE. VERMELHO, AMARELO#define bverde A1 // Os pinos analógicos podem ser#define bamarelo A2 // usados como digitais, bastando#define bvermelho A3 // referenciá-los por A0, A1..#define bDown A4
#define bverde0 90 // Valor de referência que a#define bamarelo0 91 // função CheckButton() passa#define bvermelho0 92 // indicando que um botão foi#define bDown0 93 // solto
boolean averde, aamarelo, avermelho, aDown; // Grava o ultimo valor lidos nos botões.// Utilizado pela função Checkbutton p/ identificar quando há uma alteração no estado do pino dos botões
// DEFINIÇÃO DOS PINOS E VALORES PARA O SENSOR, ESTEIRA E BOMBAint contador = 0; int Sensor = 0; int sensorPin = A0; int MotorEsteira = 11;int MotorBomb= 12;int EstadoMotor=0;int EstadoSensor=1;int LED1=8;int LED2=9;int LED3=10;int LED4=2;int buzzer=7;int variavel; // VARIAVEL A SER ALTERADA PELO MENUchar state=1; // VARIÁVEL QUE GUARDA POSIÇÃO ATUAL DO MENUint dec=-1;int i=0;
const byte LIN = 4; // TECLADO POSSUI 4 LINHASconst byte COL = 3; //TECLADO POSSUI 3 COLUNASchar keys[LIN][COL] = { {//MATRIZ CORRESPONDENTE AOS CARACTERES DO KEYPAD '1','2','3' } , { '4','5','6' } , { '7','8','9' } , { '*','0','#' }};byte LinPins[LIN] = { 28,29,30,31}; //PINOS DO ARDUINO CORRESPONDENTES AS COLUNASbyte ColPins[COL] = { 32,33,34}; //E AS LINHAS DA MATRIZ
Keypad keypad = Keypad( makeKeymap(keys), LinPins, ColPins, LIN, COL );char key = keypad.getKey();
void setup(){ lcd.begin(16, 2); // Iniciando a biblioteca do LCD Serial.begin(9600); // DEFINIÇÃO DA PORTA SERIAL
pinMode(bverde, INPUT); // Botões pinMode(bamarelo,INPUT); pinMode(bvermelho, INPUT); pinMode(bDown, INPUT);
digitalWrite(bverde, HIGH); // Aciona o pull-up interno digitalWrite(bamarelo,HIGH); // dos botões digitalWrite(bvermelho, HIGH); digitalWrite(bDown, HIGH);
pinMode(sensorPin, INPUT); //DEFINIR SE A VARIAVEL É ENTRADA(IMPUT) OU SAIDA(OUTPUT) DE DADOS pinMode(MotorEsteira, OUTPUT); pinMode(MotorBomb, OUTPUT); pinMode(EstadoSensor, INPUT); pinMode(LED1, OUTPUT); pinMode(LED2, OUTPUT); pinMode(LED3, OUTPUT); pinMode(LED4, OUTPUT); pinMode(buzzer, OUTPUT);}
void loop(){ Sensor = digitalRead(sensorPin); // A VARIAVEL SENSOR CORRESPONDE AO sensorPin QUE ESTÁ LIGADO A PORTA ANALÓGICA A0 DO ARDUIN
if(Sensor == LOW){ digitalWrite(MotorEsteira, HIGH); // SE O SENSOR NÃO ESTIVER ACIONADO O MOTOR DA BOMBA ESTA PARADO E O LED1 ASCESO digitalWrite(LED1,HIGH); digitalWrite(LED2, LOW); digitalWrite(MotorBomb, LOW); digitalWrite(buzzer, LOW); } if(Sensor == HIGH) { // SE O SENSOR ESTIVER ACIONADO O MOTOR DA ESTEIRA ESTA PARADO E O LED1 APAGA digitalWrite(MotorEsteira, LOW); // O MOTOR DA BOMBA LIGA E O LED2 ASCENDE E UM TOQUE DE 2 SEGUNDO É digitalWrite(MotorBomb, HIGH); // ACIONADO PELO BUZZER digitalWrite(LED1, LOW); digitalWrite(LED2, HIGH);
delay(8000);//ESTE DELAY SERVE PARA A BOMBA JOGAR 200ml DE AGUA NA GARRAFA contador++; Sensor == !Sensor; //O SINAL DO SENSOR É INVERTIDO PARA A FUNÇAO RETORNAR DE ONDE ESTAVA } EstadoSensor = digitalRead(sensorPin); // O SINAL DO SENSOR É MOSTRADO NO SERIAL MONITOR Serial.println(Sensor);
if(EstadoSensor == HIGH){ // A FUNÇAO RETORNA AO ESTADO INICIAL digitalWrite(MotorEsteira, HIGH); digitalWrite(MotorBomb, LOW); digitalWrite(LED1, HIGH); digitalWrite(LED2, LOW); digitalWrite(buzzer,LOW); delay(2000); }
switch (state) { // Define checa qual tela atual
case 1: // executado quando na TELA 1 switch (CheckButton()) {
case bverde: lcd.setCursor(0,0); lcd.print("Maquina Envase"); lcd.setCursor(0,2); lcd.print("Eng. Mecatronica"); delay(5000); lcd.clear(); lcd.setCursor(0,0); lcd.print("Controle Prod.");
Set_state(1); break; case bamarelo: lcd.clear(); Set_state(2); // antes de mudar de tela, é necessário limpar o break; lcd.clear(); Set_state(3); // display com a função lcd.clear() break; lcd.clear(); Set_state(1); break; default: // Caso nenhum botão tenha sido apertado, ela executa a set_state Set_state(1); // mesmo assim para atualizar o display. } break;
case 2: // executado quando na TELA 2 switch (CheckButton()) { case bverde: lcd.clear(); Set_state(1); break; case bamarelo: // lcd.clear(); Set_state(3); break; lcd.clear(); Set_state(1); break; lcd.clear(); Set_state(2); break; default: Set_state(2); } break;
case 3: // executado quando na TELA 3 switch (CheckButton()) { case bverde: lcd.clear(); Set_state(1); case bamarelo: lcd.clear(); Set_state(2); break; lcd.clear(); Set_state(3); break; lcd.clear(); Set_state(1); break; default: Set_state(3); } break;
case 4: // executado quando na TELA 4 switch (CheckButton()) { case bamarelo: lcd.clear(); Set_state(1); break; default: Set_state(1); } break;
default: ; }}// FIM DO LOOP
char CheckButton() { if (averde!=digitalRead(bverde)) { averde=!averde; if (averde) return bverde0; else return bverde; } else if (aamarelo!=digitalRead(bamarelo)) { aamarelo=!aamarelo; if (aamarelo) return bamarelo0; else return bamarelo; } else if (avermelho!=digitalRead(bvermelho)) { avermelho=!avermelho; if (avermelho) return bvermelho0; else return bvermelho; } else if (aDown!=digitalRead(bDown)) { aDown=!aDown; if (aDown) return bDown0; else return bDown; } else return 0;}
void PCP() // FUNÇÃO QUE DEFINE A QUANTIDADE DE GARRAFAS A SEREM ENVASADAS NO DIA{ char key = keypad.getKey(); if (key != NO_KEY){ lcd.setCursor(i,2); lcd.print(key); i++; }}
void Set_state(char index) { state = index; // Atualiza a variável state para a nova tela switch (state) { // verifica qual a tela atual e exibe o conteúdo correspondente
case 1: //==================== state 1 PCP(); break; case 2: //==================== state 2
lcd.setCursor(0,0); lcd.print("Qnt. Envasada:"); lcd.setCursor(0,1); lcd.print(contador); // mostra os segundos na tela lcd.print(" garrafas"); delay(20);
// if(contador==key){ // lcd.setCursor(0,0); // lcd.print("Lote Pronto!"); // lcd.setCursor(0,1); // lcd.print("Insira Novo"); // } break; case 3: //==================== state 3 lcd.setCursor(0,0); lcd.print("Nivel Tanque"); lcd.setCursor(1,2); lcd.print("%"); break; case 4: //==================== state 4
break; default: ; }}…
ratura, direto, para a saída serial.
Eu sei que um termistor NTC aumenta a resistência conforme diminui a temperatura, mas como transformar tensão (ou resistência) em temperatura?
A princípio tinha pensado em obter experimentalmente uma tabela temperatura x resistência, colocando o termistor em água gelada e medindo várias vezes a temperatura e a resistência. Mas na prática isso se mostrou mais difícil do que eu imaginava, então o melhor é mesmo utilizar o método clássico para resolver o problema.
A fórmula (bem simples) é :
RT = RT0*{e^{b*[(1/T)-(1/T0)]}}
onde:
RT é a resistência do termistor à temperatura T (medida em Kelvin).
RT0 é a resistência do termistor à temperatura T0 (medida em Kelvin).
e é o número neperiano, ou algaritmo neperiano, que a minha calculadora tá dizendo que é 2.718281828.
T é a temperatura.
T0 é a temperatura inicial.
b é a constante térmo-resistiva atribuída ao termistor, um valor obscuro que podemos obter no datasheet do termistor. O problema é que os fabricantes muitas vezes não identificam esse termistor e você fica na mão, sem conhecer a principal característica do termistor.
A boa notícia é que agora eu só preciso de 2 valores de temperatura, 2 valores de resistência e um pouquinho de matemática, encontrar a dita-cuja constante térmica e alimentar meu Arduíno com apenas uma equação.
Só que a equação não é tão simples como parece. Quem tem seus 40 anos pegou uma formação melhor em matemática, talvez os mais novos não estejam acostumados a logaritmos neperianos. Notaram que o fator "b" que procuramos é uma exponencial?
Calma! Vamos por partes. Primeiro, com um termômetro, uma caneca d'água e um multimetro vamos encontrar os valores de T, T0, RT e RT:
RT = 2.2k
T = 26ᵒC = (26+273)ᵒK = 299ᵒK (graus Kelvin)
RT0 = 8.3k
T0 = 5.5ᵒC = (5.5+273)ᵒK = 278.5ᵒK (graus Kelvin)
Então temos:
2200 = 8300*{2.718^{b*[(1/299)-(1/278.5)]}}
2200 = 8300*{2.718^{b*[-(1/4062)]}}
2200 = 8300*[2.718^(-b*/4062)]
2200/8300 = 2.718^(-b*/4062)
22/83 = 2.718^(-b*/4062)
0.265 = 2.718^(-b*/4062)
Agora tem que resolver por logaritmo:
Log b A = x <=> b^x = A
(o logaritmo de A na base b é igual a x, se e somente se: b elevado a x for igual a A)
então:
Log n (0.265) = (-b/4062)
Ln (0.265) = (-b/4062)
Agora Ln (0.265) tem que resolver na calculadora, senão é covardia:
-0.47 = -b/4062
b = 5394 ~ 5400
Pronto! Agora sabendo que R=U/i, é só jogar essa fórmula no Arduino que ele vai te retornar a temperatura em função da tensão (5 ou 3.3V) e da corrente de trabalho. Particularmente, gosto de usar planilhas e gráficos prá ajudar, mas cada um tem suas técnicas.
Só que no meu caso não é tão simples, ainda preciso concatenar a resistência do termistor (e a temperatura do ar) à frequencia de um oscilador.
Mas espero que seja útil pra vocês. Grande abraço!…
o motor de passo tem que dar uma volta completa, e conforme o andar vai mudando deverá aparecer no LCD o andar que está.
Eu comecei a fazer o programa, cada volta do motor de passo é equivalente 200 no registrador. Fiz com que a cada tecla apertada (1, 2, 3 e 4) o motor gire correspondente com a tecla. O LCD não está funcionando, no programa há outras teclas habilitadas, mas não usarei elas.
A minha dificuldade no momento é fazer com que se caso a tecla 1 for apertada e após o motor ter girado uma volta completa vier uma pessoa e apertar a tecla 2 o motor deverá girar mais uma volta completa, totalizando duas voltas e parando no andar 2. Tenho em mente em colocar algo relacionado :
JB BIT 2,(vá para uma outra instrução, que no caso no momento não é necessário saber) ;se a tecla 2 for apertada=vai para a instrução abaixo)
ANDAR1: MOV R4,#200RET0: RL A ;movendo para direita (motor andando) MOV P1,A CALL DELAY DJNZ R4,RET0 JMP STOP RET
Também preciso fazer com que o motor volte dependendo da tecla pressionada e andar atual. Pensei em fazer igual ao de cima, porém com RR A no lugar do RL A.
Eu tentei fazer dessa maneira que descrevi, porém não obtive resultado. Alguém pode me ajudar?
Segue o programa que fiz até o momento:
;#########################################################################################; Leitura de Teclado;#########################################################################################
LCD XDATA 0FF40HLINHA XDATA 0FF60HCOLUNA XDATA 0FF50HTLIDA DATA 020HBIT0 BIT 00HBIT1 BIT 01HBIT2 BIT 02HBIT3 BIT 03HBIT4 BIT 04HBIT5 BIT 05HBIT6 BIT 06HBIT7 BIT 07HK7 EQU 'A'K8 EQU 'B'K9 EQU 'C'K4 EQU 'F'K2 EQU 'D'K3 EQU 'E'K12 EQU '7'K13 EQU '8'K14 EQU '9'K17 EQU '4'K18 EQU '5'K19 EQU '6'K22 EQU '1'K23 EQU '2'K24 EQU '3'K27 EQU '#'K28 EQU '0'K29 EQU '.'
;#################################################################################; PROGRAMA PRINCIPAL;#################################################################################
MAIN: MOV SP,#25H MOV TMOD,#10H MOV TCON,#0 CLR EA
INICIO: CALL INILCD MOV A, #80H CALL CMLCD CALL LETECL CJNE A,#255, ESCREVE LJMP INICIO
ESCREVE: CALL WRLCD CALL ESPETEC LJMP INICIO ;#################################################################################; ROTINA DE LEITURA DE TECLADO;#################################################################################
LETECL: MOV A,#11111110B MOV DPTR,#LINHA MOVX @DPTR,A MOV DPTR,#COLUNA MOVX A,@DPTR MOV TLIDA,A JB BIT3,NOK2 MOV A,#K2 RET
NOK2: JB BIT2,NOK3 MOV A,#K3 RET
NOK3: JB BIT1,NOK4 MOV A,#K4 RET
NOK4: MOV A,#11111101B MOV DPTR,#LINHA MOVX @DPTR,A MOV DPTR,#COLUNA MOVX A,@DPTR MOV TLIDA,A JB BIT3,NOK7 MOV A,#K7 RET
NOK7: JB BIT2,NOK8 MOV A,#K8 RET
NOK8: JB BIT2,NOK9 MOV A,#K9 RET
NOK9: MOV A,#11111011B MOV DPTR,#LINHA MOVX @DPTR,A MOV DPTR,#COLUNA MOVX A,@DPTR MOV TLIDA,A JB BIT3,NOK12 MOV A,#K12 RET
NOK12: JB BIT2,NOK13 MOV A,#K13 RET
NOK13: JB BIT1,NOK14 MOV A,#K14 RET
NOK14: MOV A,#11110111B MOV DPTR,#LINHA MOVX @DPTR,A MOV DPTR,#COLUNA MOVX A,@DPTR MOV TLIDA,A JB BIT3,NOK17 ;se clicar na tecla 4= o motor dá 4 voltas MOV A,#K17;########################################################################; ANDAR 4;########################################################################ANDAR4: MOV R4,#200RET6: RL A ;movendo para direita (motor andando) MOV P1,A CALL DELAY DJNZ R4,RET6
ANDAR4A: MOV R4,#200RET7: RL A ;movendo para direita (motor andando) MOV P1,A CALL DELAY DJNZ R4,RET7 ANDAR4AN: MOV R4,#200RET8: RL A ;movendo para direita (motor andando) MOV P1,A CALL DELAY DJNZ R4,RET8 ANDAR4AND: MOV R4,#200RET9: RL A ;movendo para direita (motor andando) MOV P1,A CALL DELAY DJNZ R4,RET9 JMP STOP RET
;#################################################################################
NOK17: JB BIT2,NOK18 MOV A,#K18 RET
NOK18: JB BIT1,NOK19 MOV A,#K19 RET
NOK19: MOV A,#11101111B MOV DPTR,#LINHA MOVX @DPTR,A MOV DPTR,#COLUNA MOVX A,@DPTR MOV TLIDA,A JB BIT3,NOK22 ;se clicar na tecla 1= o motor dá uma volta MOV A,#K22;###############################################################################; ANDAR 1;###############################################################################
ANDAR1: MOV R4,#200RET0: RL A ;movendo para direita (motor andando) MOV P1,A CALL DELAY DJNZ R4,RET0 JMP STOP RET
;################################################################################; ANDAR 2;################################################################################NOK22: JB BIT2,NOK23 ;se clicar na tecla 2= o motor dá 2 voltas MOV A,#K23ANDAR2: MOV R4,#200RET1: RL A ;movendo para direita (motor andando) MOV P1,A CALL DELAY DJNZ R4,RET1
ANDAR2A: MOV R5,#200RET2: RL A ;movendo para direita (motor andando) MOV P1,A CALL DELAY DJNZ R5,RET2 JMP STOP RET;###############################################################################NOK23: JB BIT1,NOK24 ;se clicar na tecla 3= o motor dá 3 voltas MOV A,#K24;###############################################################################; ANDAR 3;###############################################################################ANDAR3: MOV R4,#200RET3: RL A ;movendo para direita (motor andando) MOV P1,A CALL DELAY DJNZ R4,RET3
ANDAR3A: MOV R4,#200RET4: RL A ;movendo para direita (motor andando) MOV P1,A CALL DELAY DJNZ R4,RET4 ANDAR3AN: MOV R4,#200RET5: RL A ;movendo para direita (motor andando) MOV P1,A CALL DELAY DJNZ R4,RET5 JMP STOP RET
;#################################################################################
NOK24: MOV A,11011111B MOV DPTR,#LINHA MOVX @DPTR,A MOV DPTR,#COLUNA MOVX A,@DPTR MOV TLIDA,A JB BIT3,NOK27 MOV A,#K27 RET
NOK27: JB BIT2,NOK28 MOV A,#K28 RET
NOK28: JB BIT1,NOK29 MOV A,#K29 RET
NOK29: MOV A,#255 RET ;#################################################################################; ROTINA DO INICIALIZAÇÃO DO LCD;#################################################################################
INILCD: MOV A,#039H CALL CMLCD MOV A,#006H CALL CMLCD MOV A,#001H CALL CMLCD MOV A,#00EH CALL CMLCD RET
;#################################################################################; ROTINA DE ESCRITA DE UM COMANDO PARA O DISPLAY LCD;################################################################################CMLCD: MOV DPTR,#LCD MOVX @DPTR,A MOV R6,#250CMLCDT: NOP NOP NOP NOP DJNZ R6,CMLCDT RET;#################################################################################; ROTINA DE ESCRITA DE UM CARACTER NO DISPLAY;#################################################################################WRLCD: MOV R4,DPL MOV R5,DPH MOV DPTR,#LCD INC DPTR MOVX @DPTR,A MOV R6,#200 DJNZ R6,$ MOV DPL,R4 MOV DPH,R5 RET;#################################################################################; ROTINA DE ESPERA DE 30MS E SOLTAR TECLA;#################################################################################
ESPETEC: MOV R5,#15INI: MOV TH1,#HIGH(65535-40000) MOV TL1,#LOW(65535-40000) SETB TR1 JNB TF1,$ CLR TF1 DJNZ R5,INI CLR TR1
ESPSOL: CALL LETECL CJNE A,#255,ESPSOL RET
DELAY: MOV R7,#09FHL1: MOV R6,#04FHL2: DJNZ R6,L2 DJNZ R7,L1 RET STOP: MOV A,#0 MOV R4,#0 JMP $ RET
END
…
is a Biblioteca que vc está usando sempre seleciona o modo "single-shot". Neste modo, não é o ADS1115 que determina a "real" Taxa de Amostragem, mas sim vc mesmo. Ou seja, quando vc requisita uma conversão, o ADS1115 inicia esta conversão imediatamente e após terminar a mesma ele apenas fica aguardando (ou seja, ele faz apenas aquela conversão que vc requisitou). Assim as conversões ocorrerão na Taxa que vc as requisitar.
Mas é importante entender uma coisa: o tempo que o ADS1115 leva para completar uma conversão solicitada, depende da Taxa de Amostragem que vc programou nele. Assim se por exemplo vc programou 860 Samples/seg, então o ADS levará cerca de 1.2 ms pra fazer a conversão completa. Por que isso é importante? porque quando vc requisita uma conversão e em seguida lê o valor convertido via função "readADC_SingleEnded", esta função só retorna quando o ADS tiver concluído a conversão solicitada. Então se por exemplo vc programou no ADS1115 uma taxa de 8 Samples/seg (via função "setDataRate" com o valor "RATE_ADS1115_8SPS"), o ADS levará 125 ms (1/8 de segundo) para concluir uma conversão solicitada. Assim, vc fica "preso" a essa temporização, e o efeito disso é como se seu código executasse mais lentamente, pois nenhuma outra coisa será executada até que o ADS tenha "liberado" a conversão.
Logo, no modo "single-shot", é interessante programar a taxa de 860 Samples/seg, pois o ADS levará apenas 1.2 ms pra converter e também já irá liberar seu código pra ler o resultado e executar outras coisas.
Mas há uma outra implicação muito importante caso vc use Taxas muito baixas, pois como o ADS1115 não possui "Sample & Hold" interno (não confundir com os "Capacitores chaveados" dentro do ADS1115), há a possibilidade de que ocorra um problema. Suponha que vc programe no ADS1115 uma taxa muito baixa, por exemplo de 8 Samples/seg, e que vc esteja usando mais de um canal no ADS, e que os sinais aplicados nestes canais tenham relação entre si. Agora suponha que vc vá fazer a conversão de dois canais relacionados entre si. Então vc faz a conversão do primeiro canal e em seguida faz a conversão do segundo canal. Com os dois valores convertidos, vc pode fazer os cálculos que relacionam os dois canais. Mas aí poderá ocorrer um problema: quando vc concluiu a conversão e leu o resultado do primeiro canal, como a conversão é bem lenta (125ms), o segundo canal já poderá ter mudado de valor, e assim quando vc fizer a conversão deste segundo canal, o valor resultante já não corresponderá ao valor relacionado ao primeiro canal no momento da conversão do mesmo. Ou seja, vc acaba introduzindo um "deslocamento de fase artificial", e provavelmente seus cálculos resultarão em algo que vc não espera como correto. Assim, usar Taxas mais altas para o ADS1115, evita esse problema.
Outro problema com Taxas muito baixas no ADS1115, é que durante a lenta conversão (exemplo 125ms @ 8 Samples/seg), o próprio sinal sendo atualmente convertido pode mudar, e aí a conversão deste sinal ficará comprometida.
Sobre o modo de operação do ADS1115, vc pode conferir a descrição completa no item "9.4" na página 21 do Datasheet, aqui neste Link: "ADS1115 - datasheet.pdf"
2) Ok. Ficou claro.
3) não é preciso incluir a Biblioteca "wire.h", pois a LIB do ADS1115 já faz isso (a LIB do Display LCD também já faz isso).
4) seu código "novo" ainda tem os mesmos problemas que descrevi. Mas a partir dele, eu fiz uma adequação a fim de "corrigir" estes problemas. Eu postarei o código aqui, logo depois que vc esclarecer algumas dúvidas de Hardware relativas aos sinais que vc está medindo (pois assim posso ajustar qualquer ponto relacionado).
5) ok, eu imaginava mesmo que era o UNO que vc estava usando. Mas aí, vc está "comendo bola" em um aspecto que não mencionarei neste momento (porque vc já está com todo seu setup de Hardware montado).
Sobre vc ter alimentado com 3.3V e essa ter sido a causa do problema, note que no final do meu post anterior eu adverti pra garantir que o ADS estivesse alimentado com 5V, devido às faixas de sinal que vc está convertendo (atenção também à questão do Ganho, pra evitar saturação de sinal na saída do PGA interno ao ADS1115). Dê uma olhada no item "9.3" na página 15 do Datasheet, e atente à descrição no texto e à figura 25, e então vc vai entender exatamente o que ocorreu. Mas eu não considerei que no seu teste vc tivesse alimentado com 3.3V, pois realmente achei que seu circuito estava alimentado corretamente (embora eu sempre faça a advertência como de rotina). Mas nunca ignore a questão da "Injeção de Carga", pois como eu disse antes, ela sempre ocorre.
Uma dica: lembra-se sobre não aumentar demais o valor do Capacitor Eletrolítico C2 no circuito que postei ? Então, isto também tem a ver com aqueles Diodos que vc vê na figura 25 do Datasheet. Dê uma analisada e veja se vc descobre o motivo (não é algo muito evidente).
Mas eu tenho uma desconfiança que vc pode estar usando um setup de Hardware inadequado para medir os sinais, especificamente em relação à medição da Resistência. Para que eu possa verificar isso, por favor, post aqui um desenho ou diagrama mostrando os detalhes de como vc está ligando os sinais a serem convertidos pelo ADS1115. Mas faça isso de forma clara, que não deixe dúvidas de como estão as ligações elétricas.
Assim que vc postar essa informação da ligação detalhada dos sinais, eu postarei o código onde fiz a adequação conforme mencionei neste post.
Fico no aguardo.
Abrçs,
Elcids…
Adicionado por Elcids Chagas ao 16:55 em 3 fevereiro 2022
ni. These functions implement functions 3, 6, and 16 (read holding registers, preset single register and preset multiple registers) of the Modbus RTU Protocol, to be used over the Arduino serial connection. This implementation DOES NOT fully comply with the Modbus specifications. This Arduino adaptation is derived from the work By P.Costigan email: phil@pcscada.com.au http://pcscada.com.au These library of functions are designed to enable a program send and receive data from a device that communicates using the Modbus protocol. Copyright (C) 2000 Philip Costigan P.C. SCADA LINK PTY. LTD. 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. I have used his code as a catalist to produce this more functional set of functions. Thanks paul. */
/* * configure_mb_slave(baud, parity, tx_en_pin) * * sets the communication parameters for of the serial line. * * baud: baudrate in bps (typical values 9600, 19200... 115200) * parity: a single character sets the parity mode (character frame format): * 'n' no parity (8N1); 'e' even parity (8E1), 'o' for odd parity (8O1). * tx_en_pin: arduino pin number that controls transmision/reception * of an external half-duplex device (e.g. a RS485 interface chip). * 0 or 1 disables this function (for a two-device network) * >2 for point-to-multipoint topology (e.g. several arduinos) */void configure_mb_slave(long baud, char parity, char txenpin);
/* * update_mb_slave(slave_id, holding_regs_array, number_of_regs) * * checks if there is any valid request from the modbus master. If there is, * performs the action requested * * slave: slave id (1 to 127) * regs: an array with the holding registers. They start at address 1 (master point of view) * regs_size: total number of holding registers. * returns: 0 if no request from master, * NO_REPLY (-1) if no reply is sent to the master * an exception code (1 to 4) in case of a modbus exceptions * the number of bytes sent as reply ( > 4) if OK. */
int update_mb_slave(unsigned char slave, int *regs,unsigned int regs_size);
/* HERE BEGINS THE EXAMPLE CODE */
/* Modbus RTU common parameters, the Master MUST use the same parameters */enum { COMM_BPS = 115200, /* baud rate */ MB_SLAVE = 1, /* modbus slave id */ PARITY = 'e' /* even parity */};
/* slave registers */enum { MB_CTRL, /* Led control on, off or blink */ MB_TIME, /* blink time in milliseconds */ MB_CNT, /* count the number of blinks */ MB_REGS /* total number of registers on slave */};
int regs[MB_REGS];int ledPin = 13;unsigned long wdog = 0; /* watchdog */unsigned long tprev = 0; /* previous time*/
void setup() { /* configure modbus communication * 115200 bps, 8E1, two-device network */ configure_mb_slave(COMM_BPS, PARITY, 0); pinMode(ledPin, OUTPUT);}
void loop() { /* check for master requests*/ if(update_mb_slave(MB_SLAVE, regs, MB_REGS)) wdog = millis();
if ((millis() - wdog) > 5000) { /* no comms in 5 sec */ regs[MB_CTRL] = 0; /* turn off led */ }
/* the values in regs are set by the modbus master */ switch(regs[MB_CTRL]) { case 0: digitalWrite(ledPin, LOW); break; case 1: digitalWrite(ledPin, HIGH); break; default: /* blink */ if (millis() - tprev > regs[MB_TIME]) { if (LOW == digitalRead(ledPin)) { digitalWrite(ledPin, HIGH); /* this is how you change your holding registers so the master can read values from the slave */ regs[MB_CNT]++; } else { digitalWrite(ledPin, LOW); } tprev = millis(); } }}
/**************************************************************************** * BEGIN MODBUS RTU SLAVE FUNCTIONS ****************************************************************************/
/* global variables */unsigned int Txenpin = 0; /* Enable transmission pin, used on RS485 networks */
/* enum of supported modbus function codes. If you implement a new one, put its function code here ! */enum { FC_READ_REGS = 0x03, //Read contiguous block of holding register FC_WRITE_REG = 0x06, //Write single holding register FC_WRITE_REGS = 0x10 //Write block of contiguous registers};
/* supported functions. If you implement a new one, put its function code into this array! */const unsigned char fsupported[] = { FC_READ_REGS, FC_WRITE_REG, FC_WRITE_REGS };
/* constants */enum { MAX_READ_REGS = 0x7D, MAX_WRITE_REGS = 0x7B, MAX_MESSAGE_LENGTH = 256 };
enum { RESPONSE_SIZE = 6, EXCEPTION_SIZE = 3, CHECKSUM_SIZE = 2 };
/* exceptions code */enum { NO_REPLY = -1, EXC_FUNC_CODE = 1, EXC_ADDR_RANGE = 2, EXC_REGS_QUANT = 3, EXC_EXECUTE = 4 };
/* positions inside the query/response array */enum { SLAVE = 0, FUNC, START_H, START_L, REGS_H, REGS_L, BYTE_CNT };
/*CRC INPUTS: buf -> Array containing message to be sent to controller. start -> Start of loop in crc counter, usually 0. cnt -> Amount of bytes in message being sent to controller/ OUTPUTS: temp -> Returns crc byte for message. COMMENTS: This routine calculates the crc high and low byte of a message. Note that this crc is only used for Modbus, not Modbus+ etc. ****************************************************************************/
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; } }
/* Reverse byte order. */ temp2 = temp >> 8; temp = (temp << 8) | temp2; temp &= 0xFFFF;
return (temp);}
/*********************************************************************** * * The following functions construct the required query into * a modbus query packet. * ***********************************************************************/
/* * Start of the packet of a read_holding_register response */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;}
/* * Start of the packet of a preset_multiple_register response */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;}
/* * Start of the packet of a write_single_register response */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;}
/* * Start of the packet of an exception response */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) * * Function to add a checksum to the end of a packet. * Please note that the packet array must be at least 2 fields longer than * 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 ) * * Function to send a reply to a modbus master. * Returns: total number of characters sent ************************************************************************/
int send_reply(unsigned char *query, unsigned char string_length) { unsigned char i;
if (Txenpin > 1) { // set MAX485 to speak mode UCSR0A=UCSR0A |(1 << TXC0); digitalWrite( Txenpin, HIGH); delay(1); }
modbus_reply(query, string_length); string_length += 2;
for (i = 0; i < string_length; i++) { Serial.print(query[i], BYTE); }
if (Txenpin > 1) {// set MAX485 to listen mode while (!(UCSR0A & (1 << TXC0))); digitalWrite( Txenpin, LOW); }
return i; /* it does not mean that the write was succesful, though */}
/*********************************************************************** * * receive_request( array_for_data ) * * Function to monitor for a request from the modbus master. * * Returns: Total number of characters received if OK * 0 if there is no request * A negative error code on failure ***********************************************************************/
int receive_request(unsigned char *received_string) { int bytes_received = 0;
/* FIXME: does Serial.available wait 1.5T or 3.5T before exiting the loop? */ while (Serial.available()) { received_string[bytes_received] = Serial.read(); bytes_received++; if (bytes_received >= MAX_MESSAGE_LENGTH) return NO_REPLY; /* port error */ }
return (bytes_received);}
/********************************************************************* * * modbus_request(slave_id, request_data_array) * * Function to the correct request is returned and that the checksum * is correct. * * Returns: string_length if OK * 0 if failed * Less than 0 for exception errors * * Note: All functions used for sending or receiving data via * modbus return these return values. * **********************************************************************/
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];
/*********** check CRC of response ************/ if (crc_calc != crc_received) { return NO_REPLY; }
/* check for slave id */ if (slave != data[SLAVE]) { return NO_REPLY; } } return (response_length);}
/********************************************************************* * * validate_request(request_data_array, request_length, available_regs) * * Function to check that the request can be processed by the slave. * * Returns: 0 if OK * A negative exception code on error * **********************************************************************/
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;
/* check function code */ 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]) { /* For function write single reg, this is the target reg.*/ regs_num = ((int) data[START_H] << 8) + (int) data[START_L]; if (regs_num >= regs_size) return EXC_ADDR_RANGE; return 0; } /* For functions read/write regs, this is the range. */ regs_num = ((int) data[REGS_H] << 8) + (int) data[REGS_L]; /* check quantity of registers */ 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;
/* check registers range, start address is 0 */ start_addr = ((int) data[START_H] << 8) + (int) data[START_L]; if ((start_addr + regs_num) > regs_size) return EXC_ADDR_RANGE;
return 0; /* OK, no exception */}
/************************************************************************ * * write_regs(first_register, data_array, registers_array) * * writes into the slave's holding registers the data in query, * starting at start_addr. * * Returns: the number of registers written ************************************************************************/
int write_regs(unsigned int start_addr, unsigned char *query, int *regs) { int temp; unsigned int i;
for (i = 0; i < query[REGS_L]; i++) { /* shift reg hi_byte to temp */ temp = (int) query[(BYTE_CNT + 1) + i * 2] << 8; /* OR with lo_byte */ temp = temp | (int) query[(BYTE_CNT + 2) + i * 2];
regs[start_addr + i] = temp; } return i;}
/************************************************************************ * * preset_multiple_registers(slave_id, first_register, number_of_registers, * data_array, registers_array) * * Write the data from an array into the holding registers of the slave. * *************************************************************************/
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; /* Preset Multiple Registers */ 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) * * Write a single int val into a single holding register of the slave. * *************************************************************************/
int write_single_register(unsigned char slave, unsigned int write_addr, unsigned char *query, int *regs) { unsigned char function = FC_WRITE_REG; /* Function: 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) * * reads the slave's holdings registers and sends them to the Modbus master * *************************************************************************/
int read_holding_registers(unsigned char slave, unsigned int start_addr,
unsigned char reg_count, int *regs) { unsigned char function = 0x03; /* Function 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) { // pin 0 & pin 1 are reserved for RX/TX Txenpin = txenpin; /* set global variable */ pinMode(Txenpin, OUTPUT); digitalWrite(Txenpin, LOW); }
return;}
/* * update_mb_slave(slave_id, holding_regs_array, number_of_regs) * * checks if there is any valid request from the modbus master. If there is, * performs the action requested */
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 wilton feitosa ao 13:32 em 22 fevereiro 2015
"Up" ou "Down", se seleciona quais dados são atualmente mostrados. Originalmente, eram exibidos 4 opções (Vazão instantânea em Litros/minuto, Fluxo acumulado em Litros, consumo total em metros cúbicos, e o valor da conta de água em R$), mas com a inclusão do cálculo da Vazão Média, esta informação também foi incluída para ser exibida, e dessa forma a seleção no Display tem agora 5 opções. Eu mantive a denominação original das opções, que eram chamadas de "Menu 1", "Menu 2", etc. Mas reformulei a forma como isso é implementado, para facilitar a alteração das opções (seja deletar alguma, seja acrescentar alguma outra). A definição das opções para exibir no Display, podem ser vistas na figura a seguir:
(clique na figura para "zoom")
Trata-se de uma Lista Enumerada (via statement "enum"), com a opções de exibição. A ordem de exibição é exatamente a que se encontra na Lista. Assim se for colocado "Menu_3" antes de "Menu_2", será esta a ordem de exibição. Notar que é preciso definir qual o primeiro item exibido e qual o último item exibido (neste caso o "Menu_1" e o "Menu_5"). Como explicado nos comentários no código mostrado na figura anterior, o primeiro item da Lista deve ser o valor "nenhum_item", embora este item nunca seja usado (o motivo disto é simples mas muito técnico, e por isso não entrarei em detalhes, mas caso alguém queira saber, basta perguntar).
Caso em algum momento se deseje saber no código qual o item atualmente selecionado para ser exibido no Display, basta consultar a variável "item_menu". Esta variável é definida conforme mostrado na figura a seguir:
(clique na figura para "zoom")
Na definição, também é especificado qual o valor inicial que esta variável assume quando o Sistema é iniciado (após ser ligado ou após um RESET), e claro, é o valor que foi definido para "primeiro_item_Menu".
Como dito anteriormente, também é exibido no Display, mensagens de alerta para a Vazão Limite (se houver um alerta). Esta mensagem é exibida a intervalos regulares definidos, e essa exibição dura um tempo também definido. Após terminado esse tempo de exibição, então o Sistema volta a exibir no Display o item atualmente selecionado que estava sendo exibido normalmente. O mecanismo é cíclico, sendo assim repetido no intervalo definido. A figura a seguir mostra a definição do intervalo de tempo entre exibições da mensagem de alerta e quanto tempo dura tais exibições:
(clique na figura para "zoom")
Os dois períodos de tempo estão especificados em segundos. Na definição mostrada na figura anterior, se houver um alerta, a cada 15 segundos será exibida no Display uma mensagem de alerta e cada exibição será de 3 segundos.
Para otimizar a exibição no Display, o Sistema só atualiza o mesmo quando é realmente necessário. Isto é particularmente importante se a Interface com o Display for via I2C, que é muito mais lenta que a Interface Paralela (pode ser de 10 a 40 vezes mais lenta). Para tal, apenas quando novos dados estão disponíveis, o Sistema requisita que o Display seja atualizado. Como os dados são atualizados a segundo (devido à taxa de amostragem da Vazão instantânea), então temos uma atualização do Display por segundo. A requisição é feita através de uma Flag booleana, que quando setada em "true" indica que o Display deve ser atualizado. Então quando a função que atualiza o Display detecta que tal Flag foi setada, ela faz a atualização conforme a informação atualmente sendo exibida (claro, após fazer essa detecção, esta função reseta a Flag para "false"). É esta mesma função que também verifica que uma mensagem de alerta deve ser exibida (e neste caso isso depende apenas de que o alerta esteja vigente e que seja o momento de exibir a mensagem de alerta).
A definição da Flag booleana de requisição de atualização do Display é mostrada na figura a seguir:
(clique na figura para "zoom")
O valor inicial da Flag é setado em "true", o que força uma atualização imediata do Display mesmo que uma amostragem da Vazão ainda não tenha sido obtida (embora uma atualização explícita também seja feita no "setup" do Arduino).
Como utilizar esta Flag? Quando vc expandir o código, use a mesma técnica, setando a Flag e atualizando o Display apenas quando necessário.
No código que eu publiquei no tópico anterior do J. Leonardo, para a medição da Vazão e cálculo dos parâmetros relacionados, eu implementei algo que lembrava uma Máquina de Estados para fazer estas operações. Mas a rigor eu não posso assim classificar porque nem todos os elementos de uma Máquina de Estados estavam definidos naquele código.
Nesta nova implementação, usei de fato uma Máquina de Estados para controlar aquelas mesmas operações. Isto torna o processo mais simples e permite que quaisquer mudanças no funcionamento sejam facilmente implementadas. Para isto, apenas dois estados seriam necessários: um para esperar e obter uma amostra da Vazão, e outro estado para calcular a Vazão e os demais parâmetros relacionados. Mas acrescentei um estado a mais, apenas para fazer uma inicialização sistemática do funcionamento do processo (ou seja, este estado é executado apenas uma única vez logo que a Máquina inicia, e depois não é mais executado).
A figura a seguir mostra a definição destes estados:
(clique na figura para "zoom")
O controle do processo de medição da Vazão e cálculo dos parâmetros relacionados, ou seja, a execução da Máquina de Estados, pode ser vista na figura a seguir:
(clique na figura para "zoom")
A função que executa a Máquina de Estados é a "executa_MAQ_Monitorar_Vazao", e sua chamada é feita no "loop" do Arduino. Para quem não está familiarizado com o funcionamento de Máquinas de Estados, é interessante comparar esta função com o processo do tópico "anterior" do J. Leonardo e que foi implementado no "loop" do Arduino, pois certamente isso agregará um bom know-how. Como dito, o funcionamento é muito simples, mas extremamente técnico e rigoroso, e por isso mesmo muito eficiente e confiável (ou seja, livre de "bugs").
No "setup" do Arduino, não há nenhuma novidade. Mas é importante observar a sequência em que as coisas vão sendo inicializadas para preparar o Sistema para o pleno funcionamento. Também deve-se notar a "limpeza" do processo executado no "setup", evitando-se deixar ali, linhas e linhas de código detalhado (ou talvez melhor dizer "espalhado"). Para conseguir isto, as inicializações são feitas em funções específicas para cada elemento que precisa ser inicializado, deixando claro no "setup" o que está sendo feito e a sequência em que isto é feito. Isto também facilita futuras alterações no funcionamento do Sistema, devido à melhor organização. O "setup" pode ser visto na figura a seguir:
(clique na figura para "zoom")
Finalmente, no "loop" do Arduino, graças a toda a metodologia que foi usada na implementação do restante do código, podemos ver facilmente todos os processos que são continuamente executados e que o mantém o Sistema funcionando de forma simples e robusta. Isto pode ser visto na figura a seguir, que mostra 5 processos sendo executados no "loop" do Arduino:
(clique na figura para "zoom")
É interessante notar que mesmo que se altere a sequência de execução dos processos no "loop" do Arduino, ainda assim o Sistema funcionará corretamente. Isto é devido à toda a técnica usada na implementação (como uso de Flags de sinalização, Flags de requisição, Processos "fechados", etc, além da própria Máquina de Estados). Mas obviamente, há uma sequência lógica que pode ser percebida, e o bom senso nos diz que é melhor deixar a execução seguir conforme esta sequência lógica, pois isto irá facilitar o entendimento do funcionamento do Sistema, o que facilitará muito o nosso trabalho.
Algumas considerações finais:
1) nesta implementação, observar que apenas no "setup" do Arduino e eventualmente em alguma função de inicialização (chamada no próprio "setup"), é que se utiliza a função "delay" do Arduino. Então não utilize o "delay" fora destas áreas. E cuidado com a enorme quantidade disponível na Internet, de Bibliotecas que fazem uso deliberado do "delay", provocando "travamento" da execução de todo o Sistema (infelizmente o "pessoal" parece que escreve as Bibliotecas esquecendo que o restante do Sistema precisa continuar executando).
2) sobre a simulação no Proteus, sempre é bom lembrar que dependendo do Computador, o I2C do PCF8574 pode "travar" com mais frequência. Pelas minhas investigações, o problema se restringe realmente ao PCF8574, pois com outros dispositivos I2C isto não ocorre. A performance da simulação no Proteus também cai sensivelmente quando se acessa o PCF8574, e isto afeta a simulação de outros circuitos que não tem relação alguma com o I2C. Assim a simulação do LCD via I2C no Proteus pode exigir uma certa dose de paciência.
3) sobre acrescentar o módulo Bluetooth HC-05, isto é tranquilo de fazer. Na foto que vc publicou, parece que vc conectou na Serial de Hardware do UNO (pinos "0' e "1" do UNO), a qual é "compartilhada" com a Interface USB. Isto não está errado, mas deveria ser evitado, pois causa alguns transtornos de desenvolvimento, como o fato de vc ter que desconectar o sinal TX do módulo HC-05 sempre que precisa gravar um código no UNO, além de que causa conflitos quando vc "printa" na Serial para ver no Terminal do Arduino no PC. Assim eu aconselho usar uma "SoftSerial" para evitar os problemas que mencionei. Veja: muita gente critica a "SoftSerial", mas é preciso saber usar adequadamente a mesma. Eu uso o HC-05 com SoftSerial e funciona perfeitamente via APP no Android, além de não afetar o funcionamento dos demais processos no código executando no Arduino (seja UNO, seja qualquer outro). Como há diversos detalhes sobre como fazer isso de forma adequada (tanto no código no Arduino como no APP no Android), não irei me estender sobre isso aqui, mas saiba que é perfeitamente factível usar no Arduino a "SoftSerial" com o HC-05.
O código para o Arduino, incluindo os arquivos para executar a simulação no Proteus, estão aqui: "vazao_LCD_I2C_LED_02.zip"
A Biblioteca LCD I2C que o J. Leonardo usou, é esta: "LiquidCrystal_I2C.zip"
Mas é simples alterar o código que postei, a fim de se usar outra LIB LCD I2C (por exemplo para usar a LIB I2C "padrão", no máximo duas linhas precisariam ser alteradas no código de Medição de Vazão).
Espero ter ajudado. Caso tenha alguma dúvida, não deixe de perguntar.
Abrçs,
Elcids…
Adicionado por Elcids Chagas ao 1:27 em 20 novembro 2020