Manufaturação industrial
Internet das coisas industrial | Materiais industriais | Manutenção e reparo de equipamentos | Programação industrial |
home  MfgRobots >> Manufaturação industrial >  >> Manufacturing Technology >> Processo de manufatura

Controlador do rotador de antena compatível com software de rastreamento

Componentes e suprimentos

Arduino UNO
Placa Arduino Uno
× 1
Potenciômetro rotativo (genérico)
máx. 1Kohm (500 Ohms funcionam melhor)
× 2
Codificador rotativo com botão de pressão
× 2
Breadboard (genérico)
× 1
Módulo de retransmissão (genérico)
2 módulos x 2 relés NO-Com-NC
× 2
Power MOSFET N-Channel
módulo mosfet de alimentação (mín. 12 V / 3 A)
× 2

Aplicativos e serviços online

Arduino IDE

Sobre este projeto


Última atualização novembro 2021

Este projeto começou como um entretenimento e se tornou um equipamento sério.

O Controlador aceita o posicionamento manual da antena, por meio de dois codificadores rotativos, Azimute e Elevação. Ele pode rastrear satélites automaticamente, quando conectado por USB a um PC com software de rastreamento de satélite.

É compatível com todos os softwares de rastreamento usando o protocolo EasyComm2 / 9600 bauds. PstRotator, WXtrack, HRD, MacDoppler ... Até mesmo WXtoIMG pode controlar o rotador.

Funciona diretamente com Orbitron, com um plugin DDE de http://tripsintech.com/orbitron-dde-azimuth-elevation-to-serial/

O controlador emite uma resposta em série para que o software de rastreamento exiba a posição real da antena na tela. Até agora, apenas PstRotator fez isso.

O código não usa nenhuma biblioteca (exceto para o LCD) e roda exatamente como está, com os pinos de acordo com o diagrama elétrico abaixo. Se você pressionar o botão do codificador azimute, todo o movimento da antena pára imediatamente e o comando azimute pode ser definido em 10 graus. degraus.

Você encontrará aqui duas versões:uma para motores DC e outra para motores AC (apenas relés). O último pode ter interface com rotadores de antenas comerciais existentes.

A versão com motores DC tem a vantagem de usar PWM para um movimento mais suave / suave da antena. Ele produz uma resposta de potência proporcional ao erro de ângulo (Alvo <-> Antena). Portanto, quando a antena começa a se mover, ela acelera progressivamente e, ao se aproximar da posição desejada, desacelera até a parada total. Isso é conhecido como Soft-Start / Soft-Stop . Há uma zona morta ajustável, onde a antena não se move para o menor deslocamento do alvo.

Eu tenho um beta versão com Soft-Start / Soft-Stop para motores AC, aproveitando este módulo AC-Dimmer, mas agora ele só funciona para azimute. Se você quiser experimentar, me avise por e-mail.

Se você tiver 180deg. sistema de elevação, você é bom, me dê um e-mail. Também existe uma versão com 0.1deg. precisão, mas eu não recomendaria a menos que você tenha uma leitura de potenciômetro de rocha sólida e um projeto de construção de controlador paranóico. Você encontrará mais versões em minha página da web.

Depois de terminar a construção, você deve aplicar os procedimentos de calibração .
  • A calibração do potenciômetro é obrigatório e garante a leitura correta de 0-359deg. / 0-90deg., Não importa o tipo de potenciômetro que você está usando.
  • A calibração do motor serve apenas para ajustar o Soft-Start-Stop característica. Isso é necessário se você não gosta das configurações padrão.

Explicações mais detalhadas nos vídeos. Como o código foi aprimorado com o tempo e os vídeos não podem mais ser atualizados, verifique minha página da web para obter as informações mais recentes e a experiência pessoal de viver com este controlador. https://racov.ro/index.php/2020/12/09/arduino-based-antenna-rotator-part3-software-tracking-update/

Me mande um email se quiser saber mais, pois esta plataforma não me informa sobre novos comentários, não sei por quê. Vou tentar resolver pequenos problemas o melhor que puder. [email protected]

Muito obrigado a todos que me enviaram feedback, ajudando a tornar este projeto mais confiável. Qualquer feedback é muito apreciado.






Código

  • ant-rot-DC-nov2021
  • ant-rot-AC-aug2021
  • Procedimento de calibração do potenciômetro
  • Procedimento de calibração do motor
ant-rot-DC-nov2021 Arduino
Este código é para motores DC, com saída PWM soft start-stop
 / * AZ / EL Antena Rotator controlador para Arduino - motores DC * ============================================================* Usa o protocolo EasyComm para computador - Software de Rastreamento * Comando manual por meio de dois codificadores rotativos AZ - EL * * Viorel Racoviteannu * https://www.youtube.com/channel/UCiRLZX0bV9rS04BGAyUf-fA * https://racov.ro * [email protected] * * Não posso assumir qualquer responsabilidade por uso incorreto deste código * ou qualquer tipo de dano que possa ocorrer com o uso deste código. * * dezembro de 2020 v2 - estabilidade de comunicação serial aprimorada * janeiro de 2021 - aprimorada perto da zona morta do alvo, para a qual a antena não se move * abril de 2021 - estabilidade de comunicação serial aprimorada * junho de 2021 - potência proporcional de erro para rastreamento de movimento. Real Soft-Stop * agosto 2021 - atualização USB mais rápida, direção Az / El de comutação a frio, pequenas otimizações no código * novembro 2021 - desvendou o segredo do Soft-Start. Não foi difícil. Aí está * / #include  // Biblioteca para comunicação I2C # include  // Biblioteca para LCD // Fiação:o pino SDA está conectado a A4 e o pino SCL a A5.// Conectar para o LCD via I2C, endereço padrão 0x27 (A0-A2 sem jumper) LiquidCrystal_I2C lcd (0x27, 16, 2); // endereço, caracteres, linhas.// que declara o símbolo personalizado para o byte de seta para cima / para baixo DownArrow [8] ={B00000, B00100, B00100, B00100, B10101, B01110, B00100, B00000}; byte UpArrow [8] ={B00000, B00100, B01110, B10101, B00100, B00100, B00100, B00000}; / ************************** ********** ESTE É ONDE VOCÊ REALMENTE TORNA O MOVIMENTO DA ANTENA *************** /// Potenciômetros da ANTENA CALIBRAÇÃO int AzMin =1; // início do potenciômetro int AzMax =1023; // fim do potenciômetro int ElMin =1; int ElMax =1023; // Erro permitido para o qual a antena não se move int AzErr =8; int ElErr =4; // Diferença de ângulo onde a parada suave começa int Amax =25; // azimute int Emax =15; // elevação // potência mínima e máxima para motores, porcentagens; int PwAzMin =30; // potência mínima para a qual o motor não para e dá partida sob carga int PwAzMax =100; // potência total para a velocidade mais rápida int PwElMin =30; int PwElMax =100; int PwAz =0; // potência calculada a ser transmitida ao motor (porcentagens); int PwEl =0; / ******************************************* *************************************************** ***** /// Variáveis ​​do codificador enum AzPinAssignments {AzEncoderPinA =2, // Codificador Az direito AzEncoderPinB =3, // codificador esquerdo AzClearButton =4, // encoder push ElEncoderPinA =6, // El encoder right ElEncoderPinB =5 }; // codificador esquerdo // interrompe a rotina de serviço vars unsigned int lastReportedPos =1; // gerenciamento de mudanças static boolean rotating =false; // gerenciamento de debounce booleano A_set =false; booleano B_set =false; int aState; int aLastState; // outras variáveis ​​int AzPotPin =A0; // selecione o pino de entrada para o azim. potenciômetro int AzRotPin =12; // seleciona o pino de saída para a direção de rotação int AzPWMPin =11; // selecione o pino de saída para o comando PWM azimute int TruAzim =0; // valor azimute real calculado int ComAzim =0; // valor azimute comandado int OldTruAzim =0; // para armazenar o valor azimute anterior int OldComAzim =0; char AzDir; // símbolo para exibição do azim rot int AzEncBut =1; // variável para alternar com o botão de ação do codificador int ElPotPin =A1; // selecione o pino de entrada para o elev. potenciômetro int ElRotPin =13; // selecione o pino de saída para a direção de rotação da elevação int ElPWMPin =10; // selecione o pino de saída para o comando PWM de rotação de elevação int TruElev =0; // valor de elevação real calculado int ComElev =0; // valor de elevação comandado int OldTruElev =0; // para armazenar o valor de elevação anterior int OldComElev =0; char ElDir; // símbolo para elev. rot display // sinalizadores para tolerâncias AZ, EL bool AzStop =false; bool ElStop =false; int ElUp =1; // 1 - Elevação Dn, 0 - Elevação STOP, 2 - Elevação Acima int StaAzim =0; // Ângulo de azimute inicial para motor Soft-Start int PwAzStop =0; // PWM calculado (porcentagem) para parada suave int PwAzStar =0; // PWM calculado (porcentagem) para partida suave int StaElev =0; // Ângulo de elevação inicial para motor Soft-Start int PwElStop =0; // PWM calculado (porcentagem) para parada suave int PwElStar =0; // PWM calculado (porcentagem) para início suave // ​​loop de média const int numReadings =25; int readIndex =0; // o índice da leitura atual int azimuth [numReadings]; // as leituras da entrada analógica int elevation [numReadings]; int totalAz =0; // o total em execução int totalEl =0; // variáveis ​​para comunicação serial String Azimuth =""; Elevação da coluna =""; String ComputerRead; String ComputerWrite; bool AZser =falso; bool ELser =falso; bool ANTser =false; / *************** DECLARAÇÃO DE VARIÁVEL FINAL ************ / void setup () {Serial.begin (9600); Serial.setTimeout (50); // milissegundos de espera para USB sata. Padrão 1000 // Iniciar o LCD:// lcd.begin (16,2); // selecione este se as setas não forem exibidas corretamente lcd.init (); lcd.backlight (); // escrever no nome de exibição e versão lcd.setCursor (0, 0); // Coloque o cursor na primeira linha da primeira coluna. (A contagem começa em 0!) Lcd.print ("EasyCom AntRotor"); // exibe "..." lcd.setCursor (0, 1); // Coloque o cursor na primeira coluna da segunda linha lcd.print ("* Racov * Nov.2021"); // criando um símbolo personalizado para a seta para cima / baixo lcd.createChar (1, DownArrow); lcd.createChar (2, UpArrow); // declaração do pino pinMode (AzRotPin, OUTPUT); // declarando azim. direção de rotação Pin como OUTPUT pinMode (AzPWMPin, OUTPUT); // declarando o comando PWM do azimute Pin como OUTPUT pinMode (ElRotPin, OUTPUT); // declarando elev. direção de rotação Pin como OUTPUT pinMode (ElPWMPin, OUTPUT); pinMode (AzPotPin, INPUT); pinMode (ElPotPin, INPUT); pinMode (AzEncoderPinA, INPUT); pinMode (AzEncoderPinB, INPUT); pinMode (AzClearButton, INPUT); pinMode (ElEncoderPinA, INPUT); pinMode (ElEncoderPinB, INPUT); // Pino AzEncoder na interrupção 0 (pino A) attachInterrupt (0, doEncoderA, CHANGE); // Pino AzEncoder na interrupção 1 (pino B) attachInterrupt (1, doEncoderB, CHANGE); // Lê o estado inicial do ElEncoderPinA aLastState =digitalRead (ElEncoderPinA); / * inicialização do loop de média * / TruAzim =(map (analogRead (AzPotPin), AzMin, AzMax, 0, 359)); // valor azimute 0-359 if (TruAzim <0) {TruAzim =0;} if (TruAzim> 359) {TruAzim =359;} // mantém os valores entre os limites TruElev =(map (analogRead (ElPotPin), ElMin, ElMax , 0, 90)); // elev valor 0-90 if (TruElev <0) {TruElev =0;} if (TruElev> 90) {TruElev =90;} // manter os valores entre os limites para (int thisReading =0; thisReading  359) {TruAzim =359;} if (TruElev <0) {TruElev =0;} if (TruElev> 90) {TruElev =90;} // avança para a próxima posição na matriz:readIndex =readIndex + 1; // se estivermos no final do array, volte ao início:if (readIndex> =numReadings) {readIndex =0;} // isso é para ler o comando do codificador ReadAzimEncoder (); ReadElevEncoder (); if (Serial.available ()) {SerComm ();} // ler dados USB // atualizar exibição de posição da antena apenas se o valor mudar if ((millis ()% 500) <10) {// não piscar a tela se (OldTruAzim! =TruAzim) {DisplAzim (TruAzim, 4,0); OldTruAzim =TruAzim; } if (OldTruElev! =TruElev) {DisplElev (TruElev, 5,1); OldTruElev =TruElev; }} // atualiza a exibição da posição alvo apenas se o valor mudar if (OldComAzim! =ComAzim) {DisplAzim (ComAzim, 12,0); OldComAzim =ComAzim; } if (OldComElev! =ComElev) {DisplElev (ComElev, 13,1); OldComElev =ComElev; } // isso é para girar em azimute if (TruAzim ==ComAzim) {// se igual, pare de mover AzStop =true; analogWrite (AzPWMPin, 0); // Potência do motor Az =0 StaAzim =TruAzim; // este será o azimute inicial para soft-start lcd.setCursor (8, 0); lcd.print ("="); } else if ((abs (TruAzim - ComAzim) <=AzErr) &&(AzStop ==false)) {// se em tolerância, mas não era igual, girar AzimRotate ();} else if (abs (TruAzim - ComAzim)> AzErr) {// se o alvo estiver fora da tolerância AzStop =false; // não é igual AzimRotate (); // girar} // isso é para girar na elevação if (TruElev ==ComElev) {// se igual, pare de se mover ElStop =true; analogWrite (ElPWMPin, 0); // El motor power =0 StaElev =TruElev; // esta será a elevação inicial para soft-start lcd.setCursor (8, 1); lcd.print ("="); ElUp =0; // sinalizar para elevação STOP} else if ((abs (TruElev - ComElev) <=ElErr) &&(ElStop ==false)) {// se em tolerância, mas não era igual, girar ElevRotate ();} else if (abs (TruElev - ComElev)> ElErr) {// se o alvo está fora da tolerância ElStop =false; // não é igual ElevRotate (); // rotate} // para interpretar a multiplicação x10 do codificador Az while (AzEncBut ==10) {// while alternado para x10 analogWrite (AzPWMPin, 0); // PARAR a rotação da antena StaAzim =TruAzim; // este será o azimute inicial para soft-start analogWrite (ElPWMPin, 0); lcd.setCursor (8, 0); lcd.print ("*"); ReadAzimEncoder (); if (OldComAzim! =ComAzim) {// atualiza a exibição apenas se os números mudarem DisplAzim (ComAzim, 12, 0); OldComAzim =ComAzim; } atraso (100); }} // fim do LOOP principal // ____________________________________________________ // ___________ definições de procedimentos__________________void DisplAzim (int x, int y, int z) {char displayString [7] =""; sprintf (displayString, "% 03d", x); // gera um número de comprimento fixo (3 inteiros) lcd.setCursor (y, z); // sem zeros à esquerda "__7" use "% 3d" lcd.print (displayString); // ************** PARA FINS DE CALIBRAÇÃO ************** // Serial.print ("Az"); // Serial.println ( analogRead (AzPotPin));} void DisplElev (int x, int y, int z) {char displayString [7] =""; sprintf (displayString, "% 02d", x); // gera um número de comprimento fixo (2 inteiros) lcd.setCursor (y, z); // sem zeros à esquerda "_7", use "% 2d" lcd.print (displayString); // ************** PARA FINS DE CALIBRAÇÃO ********** **** // Serial.print ("El"); // Serial.println (analogRead (ElPotPin));} void ReadElevEncoder () {aState =digitalRead (ElEncoderPinA); // Lê o estado "atual" do ElEncoderPinA // Se o estado anterior e o atual do ElEncoderPinA forem diferentes, isso significa que ocorreu um Pulso if (aState! =ALastState) {// Se o estado ElEncoderPinB for diferente do Estado ElEncoderPinA, que significa que o codificador está girando no sentido horário if (digitalRead (ElEncoderPinB)! =AState) {ComElev ++;} else {ComElev -;} if (ComElev <0) {ComElev =0;} if (ComElev> 90 ) {ComElev =90;}} aLastState =aState; // Atualiza o estado anterior do ElEncoderPinA com o estado atual} void ReadAzimEncoder () {rotating =true; // redefine o debouncer if (lastReportedPos! =ComAzim) {lastReportedPos =ComAzim; } atraso (10); if (digitalRead (AzClearButton) ==LOW) {// se o interruptor do codificador pressionou o atraso (250); // debounce switch if (AzEncBut ==1) {AzEncBut =10; ComAzim =int (ComAzim / 10) * 10; // ComAzim em 10deg. etapas} else {AzEncBut =1; }}} // fim ReadAzimEncoder () // Interromper em um statevoid em mudança doEncoderA () {// debounce if (rotating) delay (1); // espere um pouco até que o salto seja concluído // Teste a transição, as coisas realmente mudaram? if (digitalRead (AzEncoderPinA)! =A_set) {// debounce mais uma vez A_set =! A_set; // ajusta o contador + se A liderar B if (A_set &&! B_set) ComAzim + =AzEncBut; ComAzim =((ComAzim + 360)% 360); // encoderPos entre 0 e 359 graus. girando =falso; // sem mais debouncing até que loop () atinja novamente}} // Interrompe na mudança de estado de B, o mesmo que A abovevoid doEncoderB () {if (rotating) delay (1); if (digitalRead (AzEncoderPinB)! =B_set) {B_set =! B_set; // ajusta o contador - 1 se B lidera A if (B_set &&! A_set) ComAzim - =AzEncBut; ComAzim =((ComAzim + 360)% 360); // encoderPos entre 0 e 359 graus. girando =falso; }} void AzimRotate () {if (ComAzim> TruAzim) {// para determinar a direção de rotação // comutação a frio - pare o motor antes de mudar de direção - para proteger as peças mecânicas e elétricas if (AzDir ==char (127)) { // se girando anteriormente na direção oposta analogWrite (AzPWMPin, 0); // PARE o motor StaAzim =TruAzim; // este será o azimute inicial para atraso de início suave (200); // atraso pré-switch digitalWrite (AzRotPin, LOW); // desativa o pino de rotação - rotate right delay (200); // atraso pós-switch} else {// mesma direção, sem parada, sem atraso digitalWrite (AzRotPin, LOW); // desativa o pino de rotação - gira para a direita} AzDir =char (126); // "->"} else {if (AzDir ==char (126)) {// se girando anteriormente na direção oposta analogWrite (AzPWMPin, 0); // PARE o motor StaAzim =TruAzim; // este será o azimute inicial para atraso de início suave (200); // atraso pré-switch digitalWrite (AzRotPin, HIGH); // ativa o pino de rotação - rotação para a esquerda delay (200); // atraso pós-switch} else {// mesma direção, sem parada, sem atraso digitalWrite (AzRotPin, HIGH); // ativa o pino de rotação - gira para a esquerda} AzDir =char (127); // "<-"} lcd.setCursor (8, 0); lcd.print (String (AzDir)); // isso ativa o pino PWM do azim proporcional ao erro de ângulo (calculado em porcentagem%) PwAzStop =PwAzMin + round ((abs (ComAzim-TruAzim)) * (PwAzMax-PwAzMin) / Amax); // fórmula que produz uma potência proporcional com diferença de ângulo para Soft-Stop PwAzStar =PwAzMin + round ((abs (StaAzim-TruAzim)) * (PwAzMax-PwAzMin) / Amax); // fórmula que produz uma potência proporcional com diferença angular para Soft-Start if (PwAzStar> PwAzStop) {PwAz =PwAzStop; // escolha o valor que for menor} else {PwAz =PwAzStar;} if (PwAz> PwAzMax) {PwAz =PwAzMax;} analogWrite (AzPWMPin, round (2.55 * PwAz)); // ativa o pino PWM da unidade Azim} // fim AzimRotate () void ElevRotate () {// para determinar a direção de rotação if (ComElev> TruElev) {if (ElUp ==1) {// se girando anteriormente no oposto direção analogWrite (ElPWMPin, 0); // PARE o motor StaElev =TruElev; // esta será a elevação inicial para atraso de início suave (200); // atraso pré-switch digitalWrite (ElRotPin, LOW); // desativa o pino de rotação - rotate UP delay (200); // atraso pós-switch} else {// mesma direção, sem parada, sem atraso digitalWrite (ElRotPin, LOW); // desativa o pino de rotação - gire para CIMA} lcd.setCursor (8, 1); lcd.write (2); // seta para cima ElUp =2; // sinaliza para elevação UP} else {if (ElUp ==2) {// se girando anteriormente na direção oposta analogWrite (ElPWMPin, 0); // PARE o motor StaElev =TruElev; // esta será a elevação inicial para atraso de início suave (200); // atraso pré-switch digitalWrite (ElRotPin, HIGH); // desativa o pino de rotação - rotate UP delay (200); // atraso pós-switch} else {// mesma direção, sem parada, sem atraso digitalWrite (ElRotPin, HIGH); // desativa o pino de rotação - gire para CIMA} lcd.setCursor (8, 1); lcd.write (1); // seta para baixo ElUp =1; // sinaliza para elevação DN} // ativa o pino PWM do azim proporcional ao erro de ângulo (calculado em porcentagem%) PwElStop =PwElMin + round ((abs (ComElev-TruElev)) * (PwElMax-PwElMin) / Emax); // fórmula que produz uma potência proporcional com diferença de ângulo para Soft-Stop PwElStar =PwElMin + round ((abs (StaElev-TruElev)) * (PwElMax-PwElMin) / Emax); // fórmula que produz uma potência proporcional com diferença angular para Soft-Start if (PwElStar> PwElStop) {PwEl =PwElStop; // escolha o valor que for menor} else {PwEl =PwElStar;} if (PwEl> PwElMax) {PwEl =PwElMax;} analogWrite (ElPWMPin, round (2.55 * PwEl)); // ativa o pino PWM da unidade Elev} // fim ElevRotate () void SerComm () {// inicializa as leituras ComputerRead =""; Azimute =""; Elevação =""; while (Serial.available ()) {ComputerRead =Serial.readString (); // ler os dados de entrada como string // Serial.println (ComputerRead); // ecoar a recepção para fins de teste} // procurando o comando  para (int i =0; i <=ComputerRead.length (); i ++) {if ((ComputerRead.charAt (i) ==' A ') &&(ComputerRead.charAt (i + 1) ==' Z ')) {// se ler AZ para (int j =i + 2; j <=ComputerRead.length (); j ++) {if (isDigit (ComputerRead.charAt (j))) {// se o caractere for o número Azimuth =Azimuth + ComputerRead.charAt (j); } else {break;}}}} // procurando o comando  para (int i =0; i <=(ComputerRead.length () - 2); i ++) {if ((ComputerRead.charAt (i ) =='E') &&(ComputerRead.charAt (i + 1) =='L')) {// se lido EL if ((ComputerRead.charAt (i + 2)) =='-') {ComElev =0; // se a elevação for negativa, quebra; } for (int j =i + 2; j <=ComputerRead.length (); j ++) {if (isDigit (ComputerRead.charAt (j))) {// se o caractere é o número Elevation =Elevation + ComputerRead.charAt ( j); } else {break;}}}} // if  recebido if (Azimuth! ="") {ComAzim =Azimuth.toInt (); ComAzim =ComAzim% 360; // mantendo os valores entre os limites (para rastreadores com mais de 360 ​​graus de rotação)} // if  recebido if (Elevation! ="") {ComElev =Elevation.toInt (); if (ComElev> 180) {ComElev =0;} if (ComElev> 90) {// se recebido mais de 90deg. (para rastreadores com elevação de 180 graus) ComElev =180-ComElev; // mantenha-se abaixo de 90 graus. ComAzim =(ComAzim + 180)% 360; // e gire a antena na parte de trás}} // procurando  interrogação para a posição da antena para (int i =0; i <=(ComputerRead.length () - 4); i ++) {if ((ComputerRead .charAt (i) =='A') &&(ComputerRead.charAt (i + 1) =='Z') &&(ComputerRead.charAt (i + 3) =='E') &&(ComputerRead.charAt (i +4) =='L')) {// devolve a posição da antena <+ xxx.x xx.x> ComputerWrite ="+" + String (TruAzim) + ". 0" + String (TruElev) + ". 0 "; Serial.println (ComputerWrite); }}} // fim de SerComm () 
ant-rot-AC-aug2021 Arduino
Certifique-se de usar o diagrama elétrico para motores AC
Oferece contatos secos (ON / OFF). Pode ser facilmente conectado a rotadores comerciais.
 / * AZ / EL Antenna Rotator controlador para Arduino - motores AC * ==============================================================* Utiliza protocolo EasyComm para computador - Software de Rastreamento * Comando manual por meio de dois codificadores rotativos AZ - EL * * compatível com rotadores de caixa de distribuição * ou motores AC * contatcts secos para esquerda-direita, cima-baixo * * Viorel Racoviteannu / * https://www.youtube.com/channel/UCiRLZX0bV9rS04BGAyUf-fA * https://racov.ro * [email protected] * * Não posso assumir qualquer responsabilidade pelo uso indevido deste código * ou qualquer tipo de dano que possa ocorrer com o uso deste código. * * dezembro de 2020 v2 - estabilidade de comunicação serial aprimorada * janeiro de 2021 - tolerâncias de AZ e EL fixas para ativação do motor * abril de 2021 - estabilidade de comunicação serial aprimorada * agosto de 2021 - atualização USB mais rápida, comutação a frio direção Az / El, pequenas otimizações no código * / #include  // Biblioteca para comunicação I2C # include  // https://www.arduinolibraries.info/libraries/liquid-crystal-i2-c (Biblioteca para LCD) // Fiação:o pino SDA está conectado a A4 e o pino SCL a A5.// Conecte ao LCD via I2C, endereço padrão 0x27 (A0-A2 sem jumpers) LiquidCrystal_I2C lcd (0x27, 16, 2); // endereço, caracteres, linhas.// que declara o símbolo personalizado para o byte de seta para cima / para baixo DownArrow [8] ={B00000, B00100, B00100, B00100, B10101, B01110, B00100, B00000}; byte UpArrow [8] ={B00000, B00100, B01110, B10101, B00100, B00100, B00100, B00000}; // Potenciômetros de ANTENA CALIBRAÇÃO int AzMin =1; // início do potenciômetro int AzMax =1023; // fim do potenciômetro int ElMin =1; int ElMax =1023; // Erro permitido para o qual a antena não se move int AzErr =8; int ElErr =4; // Variáveis ​​do codificador Azim enum AzPinAssignments {AzEncoderPinA =2, // codificador direito AzEncoderPinB =3, // codificador esquerdo AzClearButton =4}; // encoder push unsigned int lastReportedPos =1; // gerenciamento de mudanças static boolean rotating =false; // gerenciamento de depuração // interrompe a rotina de serviço vars boolean A_set =false; booleano B_set =false; // Elev encoder variables enum ElPinAssignments {ElEncoderPinA =6, // encoder right ElEncoderPinB =5, // encoder left ElClearButton =7}; // encoder push int aState; int aLastState; // outras variáveis ​​int AzPotPin =A0; // selecione o pino de entrada para o azim. potenciômetro int AzRotPinR =13; // seleciona o pino de saída para a direção de rotação int AzRotPinL =12; int TruAzim =0; // valor azimute real calculado int ComAzim =0; // valor azimute comandado int OldTruAzim =0; // para armazenar o valor azimute anterior int OldComAzim =0; char AzDir; // símbolo para exibição do azim rot int AzEncBut =1; // variável para alternar com o botão de ação do codificador int ElPotPin =A1; // selecione o pino de entrada para o elev. potenciômetro int ElRotPinD =11; // seleciona o pino de saída para a direção de rotação da elevação int ElRotPinU =10; int TruElev =0; // valor de elevação real calculado int ComElev =0; // valor de elevação comandado int OldTruElev =0; // para armazenar o valor de elevação anterior int OldComElev =0; char ElDir; // símbolo para elev. rot display int ElEncBut =1; // variável para alternar com o botão de ação do codificador // sinalizadores para tolerâncias AZ, EL bool AzStop =false; bool ElStop =false; int ElUp =0; // 1 =Elevação Dn, 0 =Elevação STOP, 2 =Elevação Acima // loop de média const int numReadings =25; int readIndex =0; // o índice da leitura atual int azimuth [numReadings]; // as leituras da entrada analógica int elevation [numReadings]; int totalAz =0; // o total em execução int totalEl =0; // variáveis ​​para comunicação serial String Azimuth =""; Elevação da coluna =""; String ComputerRead; String ComputerWrite; bool AZser =falso; bool ELser =falso; bool ANTser =false; / *************** DECLARAÇÃO DE VARIÁVEL FINAL ************ / void setup () {Serial.begin (9600); Serial.setTimeout (50); // milissegundos de espera para USB sata. Padrão 1000 // Iniciar o LCD:// lcd.begin (16,2); // selecione este se as setas não forem exibidas corretamente lcd.init (); lcd.backlight (); // escrever no nome de exibição e versão lcd.setCursor (0, 0); // Coloque o cursor na primeira linha da primeira coluna. (A contagem começa em 0!) Lcd.print ("EasyCom AntRotor"); lcd.setCursor (0, 1); // Coloque o cursor na primeira coluna da segunda linha lcd.print ("* Racov * Ago.2021"); // criando um símbolo personalizado para a seta para cima / baixo lcd.createChar (1, DownArrow); lcd.createChar (2, UpArrow); // declaração do pino pinMode (AzRotPinR, OUTPUT); // declarando azim. direção de rotação Pin como OUTPUT pinMode (AzRotPinL, OUTPUT); pinMode (ElRotPinD, OUTPUT); // declarando elev. direção de rotação Pin como OUTPUT pinMode (ElRotPinU, OUTPUT); pinMode (AzPotPin, INPUT); pinMode (ElPotPin, INPUT); pinMode (AzEncoderPinA, INPUT); pinMode (AzEncoderPinB, INPUT); pinMode (AzClearButton, INPUT); pinMode (ElEncoderPinA, INPUT); pinMode (ElEncoderPinB, INPUT); pinMode (ElClearButton, INPUT); // Pino AzEncoder na interrupção 0 (pino A) attachInterrupt (0, doEncoderA, CHANGE); // Pino AzEncoder na interrupção 1 (pino B) attachInterrupt (1, doEncoderB, CHANGE); // Leituras o estado inicial do ElEncoderPinA aLastState =digitalRead (ElEncoderPinA); / * inicialização do loop de média * / TruAzim =(map (analogRead (AzPotPin), AzMin, AzMax, 0, 359)); // valor azimute 0-359 if (TruAzim <0) {TruAzim =0;} if (TruAzim> 359) {TruAzim =359;} // mantém os valores entre os limites TruElev =(map (analogRead (ElPotPin), ElMin, ElMax , 0, 90)); // elev valor 0-90 if (TruElev <0) {TruElev =0;} if (TruElev> 90) {TruElev =90;} // manter os valores entre os limites para (int thisReading =0; thisReading  359) {TruAzim =359;} if (TruElev <0) {TruElev =0;} if (TruElev> 90) {TruElev =90;} // avança para a próxima posição na matriz:readIndex =readIndex + 1; // se estivermos no final do array, volte ao início:if (readIndex> =numReadings) {readIndex =0;} // isso é para ler o comando do codificador ReadAzimEncoder (); ReadElevEncoder (); if (Serial.available ()) {SerComm ();} // ler dados USB // atualizar exibição de posição da antena apenas se o valor mudar if ((millis ()% 500) <10) {// não piscar a tela se (OldTruAzim! =TruAzim) {DisplAzim (TruAzim, 4,0); OldTruAzim =TruAzim; } if (OldTruElev! =TruElev) {DisplElev (TruElev, 5,1); OldTruElev =TruElev; } }// update target position display only if value change if (OldComAzim !=ComAzim) { DisplAzim(ComAzim,12,0); OldComAzim =ComAzim; } if (OldComElev !=ComElev) { DisplElev(ComElev,13,1); OldComElev =ComElev; }// this is to rotate in azimuth if (TruAzim ==ComAzim) { // if equal, stop moving AzStop =true; digitalWrite(AzRotPinL, LOW); // deactivate rotation pin digitalWrite(AzRotPinR, LOW); lcd.setCursor(8, 0); lcd.print("="); } else if ((abs(TruAzim - ComAzim)<=AzErr)&&(AzStop ==false)) { // if in tolerance, but it wasn't an equal, rotate AzimRotate();} else if (abs(TruAzim - ComAzim)>AzErr){ // if target is off tolerance AzStop =false; // it's not equal AzimRotate(); // rotate }// this is to rotate in elevation if (TruElev ==ComElev) { // if equal, stop moving ElStop =true; digitalWrite(ElRotPinD, LOW); // deactivate elevator pin digitalWrite(ElRotPinU, LOW); lcd.setCursor(8, 1); lcd.print("="); ElUp =0; // flag for elevation STOP } else if ((abs(TruElev - ComElev)<=ElErr)&&(ElStop ==false)) { // if in tolerance, but it wasn't an equal, rotate ElevRotate();} else if (abs(TruElev - ComElev)>ElErr){ // if target is off tolerance ElStop =false; // it's not equal ElevRotate(); // rotate }// this is to interpret x10 AZ ENC multiplication while (AzEncBut ==10) { // while toggled to x10 digitalWrite(AzRotPinL, LOW); // deactivate rotation pin digitalWrite(AzRotPinR, LOW); digitalWrite(ElRotPinD, LOW); // deactivate elevator pin digitalWrite(ElRotPinU, LOW); lcd.setCursor(8, 0); lcd.print ("*"); ReadAzimEncoder(); if (OldComAzim !=ComAzim){ // update display only if numbers change DisplAzim(ComAzim, 12, 0); OldComAzim =ComAzim; } atraso (100); }}// end main LOOP//____________________________________________________// ___________procedures definitions__________________void DisplAzim(int x, int y, int z) { char displayString[7] =""; sprintf(displayString, "%03d", x); //outputs a fixed lenght number (3 integer) lcd.setCursor(y, z); // for no leading zeros "__7" use "%3d" lcd.print(displayString); // ************** FOR CALIBRATION PURPOSES **************// Serial.print ("Az ");// Serial.println (analogRead(AzPotPin));}void DisplElev(int x, int y, int z){ char displayString[7] =""; sprintf(displayString, "%02d", x); //outputs a fixed lenght number (2 integer) lcd.setCursor(y, z); // for no leading zeros "_7" use "%2d" lcd.print(displayString);// ************** FOR CALIBRATION PURPOSES **************// Serial.print ("El ");// Serial.println (analogRead(ElPotPin));}void ReadElevEncoder() { aState =digitalRead(ElEncoderPinA); // Reads the "current" state of the ElEncoderPinA // If the previous and the current state of the ElEncoderPinA are different, that means a Pulse has occured if (aState !=aLastState){ // If the ElEncoderPinB state is different to the ElEncoderPinA state, that means the encoder is rotating clockwise if (digitalRead(ElEncoderPinB) !=aState) { ComElev ++;} else { ComElev --;} if (ComElev <0) {ComElev =0;} if (ComElev>90) {ComElev =90;} } aLastState =aState; // Updates the previous state of the ElEncoderPinA with the current state}void ReadAzimEncoder() { rotating =true; // reset the debouncer if (lastReportedPos !=ComAzim) { lastReportedPos =ComAzim; } delay(10); if (digitalRead(AzClearButton) ==LOW ) { // if encoder switch depressed delay (250); // debounce switch if (AzEncBut ==1){ AzEncBut =10; ComAzim =int(ComAzim/10)*10; // ComAzim in 10deg. steps } else { AzEncBut =1; } }} //end ReadAzimEncoder()// Interrupt on A changing statevoid doEncoderA() { // debounce if ( rotating ) delay (1); // wait a little until the bouncing is done // Test transition, did things really change? if ( digitalRead(AzEncoderPinA) !=A_set ) { // debounce once more A_set =!A_set; // adjust counter + if A leads B if ( A_set &&!B_set ) ComAzim +=AzEncBut; ComAzim =((ComAzim + 360) % 360); // encoderPos between 0 and 359 deg. rotating =false; // no more debouncing until loop() hits again }}// Interrupt on B changing state, same as A abovevoid doEncoderB() { if ( rotating ) delay (1); if ( digitalRead(AzEncoderPinB) !=B_set ) { B_set =!B_set; // adjust counter - 1 if B leads A if ( B_set &&!A_set ) ComAzim -=AzEncBut; ComAzim =((ComAzim + 360) % 360); // encoderPos between 0 and 359 deg. rotating =false; } }void AzimRotate() { if ((ComAzim-TruAzim)> (TruAzim-ComAzim)) { // this to determine direction of rotation// cold switching - stop motor before changing direction - to protect mechanic and electric parts digitalWrite(AzRotPinL, LOW); // deactivate rotation pin Left if (AzDir ==char(127)) {delay(500);} // if previously rotating in the oposite direction, wait 0.5 seconds digitalWrite(AzRotPinR, HIGH); // activate rotation pin Right AzDir =char(126); // "->" } else { digitalWrite(AzRotPinR, LOW); if (AzDir ==char(126)) {delay(500);} digitalWrite(AzRotPinL, HIGH); AzDir =char(127); // "<-" } lcd.setCursor(8, 0); lcd.print(String(AzDir));}void ElevRotate() {// this to determine direction of rotation if ((ComElev-TruElev)> (TruElev-ComElev)) { digitalWrite(ElRotPinD, LOW); if (ElUp ==1) {delay(500);} digitalWrite(ElRotPinU, HIGH); lcd.setCursor(8, 1); lcd.write (2); // arrow up ElUp =2; } else { digitalWrite(ElRotPinU, LOW); if (ElUp ==2) {delay(500);} digitalWrite(ElRotPinD, HIGH); lcd.setCursor(8, 1); lcd.write (1); // arrow down ElUp =1; }}void SerComm() { // initialize readings ComputerRead =""; Azimuth =""; Elevation =""; while(Serial.available()) { ComputerRead=Serial.readString(); // read the incoming data as string Serial.println(ComputerRead); // echo the reception for testing purposes } // looking for command  for (int i =0; i <=ComputerRead.length(); i++) { if ((ComputerRead.charAt(i) =='A')&&(ComputerRead.charAt(i+1) =='Z')){ // if read AZ for (int j =i+2; j <=ComputerRead.length(); j++) { if (isDigit(ComputerRead.charAt(j))) { // if the character is number Azimuth =Azimuth + ComputerRead.charAt(j); } else {break;} } } } // looking for command  for (int i =0; i <=(ComputerRead.length()-2); i++) { if ((ComputerRead.charAt(i) =='E')&&(ComputerRead.charAt(i+1) =='L')){ // if read EL if ((ComputerRead.charAt(i+2)) =='-') { ComElev =0; // if elevation negative break; } for (int j =i+2; j <=ComputerRead.length(); j++) { if (isDigit(ComputerRead.charAt(j))) { // if the character is number Elevation =Elevation + ComputerRead.charAt(j); } else {break;} } } } // if  received if (Azimuth !=""){ ComAzim =Azimuth.toInt(); ComAzim =ComAzim%360; // keeping values between limits }// if  received if (Elevation !=""){ ComElev =Elevation.toInt(); if (ComElev>180) { ComElev =0;} if (ComElev>90) { //if received more than 90deg. (for trackers with 180deg. elevation) ComElev =180-ComElev; //keep below 90deg. ComAzim =(ComAzim+180)%360; //and rotate the antenna on the back } }// looking for  interogation for antenna position for (int i =0; i <=(ComputerRead.length()-4); i++) { if ((ComputerRead.charAt(i) =='A')&&(ComputerRead.charAt(i+1) =='Z')&&(ComputerRead.charAt(i+3) =='E')&&(ComputerRead.charAt(i+4) =='L')){ // send back the antenna position <+xxx.x xx.x> ComputerWrite ="+"+String(TruAzim)+".0 "+String(TruElev)+".0"; Serial.println(ComputerWrite); } }}// end SerComm()
Potentiometer calibration procedureArduino
AZ / EL Potentiometers limit calibration PROCEDURE for displaying the correct antenna angles and rotation limits ( 0-359ᴼ / 0-90ᴼ)
This is plain text, not a code :)
AZ / EL Potentiometers limit calibration PROCEDURE ( 0-359ᴼ / 0-90ᴼ)This might seem complicated, but it only has to be done once.1. Open the code in Arduino and - Look for void DisplAzim(int x, int y, int z) {...// Serial.print ("Az ");// Serial.println (analogRead(AzPotPin)); - Uncoment these lines. Should look like this:Serial.print ("Az "); Serial.println (analogRead(AzPotPin)); - Look for void DisplElev(int x, int y, int z){...// Serial.print ("El ");// Serial.println (analogRead(ElPotPin));Uncoment these lines, too. Should look like this:Serial.print ("El "); Serial.println (analogRead(ElPotPin));2. Upload the code and open the Serial Monitor. There you will see a lot of numbers;3. With the help of the encoders, move the antenna to minimum values, 0ᴼ in azimuth and 0ᴼ in elevation.- Write down the values for Azimuth and Elevation. (in my case it was AzMin=90, ElMin=10)- These are the input values read by Arduino, not the real angles;4. Move the antenna again to maximum values, 359ᴼ in azimuth and 90ᴼ in elevation.- Again, write down the values for Azimuth and Elevation. (in my case it was AzMax=1000, ElMax=992);5. Look in the code, at the beginning, for the section// ANTENNA potentiometers CALIBRATION int AzMin =1; int AzMax =1023; int ElMin =1; int ElMax =1023;- Here input the values you wrote down for each situation;6. Now it is no longer necessary to send this on serial, so you have to comment back these lines, like this:// Serial.print ("Az "); // Serial.println (analogRead(AzPotPin));... // Serial.print ("El "); // Serial.println (analogRead(ElPotPin));7. Upload again the code.That's all.Now, in the serial monitor, there should be no more numbers, and the true antenna position is read correctly.
Motor calibration procedureArduino
This procedure sets the parameters for the Antenna Speed-Up / Slow-Down Zone.
This is plain text, not a code :)
Motor Calibration Procedure For Soft-Start / Soft-Stop feature.This procedure sets the parameters for the Antenna Speed-Up / Slow-Down and the Dead-Zone.You basically set how fast and how slow you want the antenna to start and to stop. You also set much the target can move, before the antenna will adjust again.It’s not strictly necessary, only if you don’t like the default settings.Make sure you first apply the Potentiometer Calibration Procedure !!! That one is strictly necessary.Look at the power diagram for a better understanding.***For Azimuth movement***-As the antenna starts to move towards the target, is picking up speed, reaching full power after  degrees difference. -As the antenna closes in to the target, below  degrees difference, it starts to slow down.  should be higher for heavier antennas.-The power starts to decrease from  to  until the angle difference becomes zero.  (in percents %) should be 100 for full speed. If you ever think your antenna rotates too fast, you can set a smaller .  (in percents %) is the minimum power for which your motor doesn’t stall and can start under load. The power output never falls below this value.-Once the antenna reaches the target position (zero degrees error), it stops and doesn’t move again until the target travels more than  degrees. This is a dead zone, to prevent continuously shaking the antenna for the smallest target movement, or potentiometer position jitter. The smaller the , the more precise tracking, the more frequent shacking of the motors.***For Elevation movement***Exactly as for the Azimuth.Look at the beginning of the code for this section. Here you can input your desired values./**************THIS IS WHERE YOU REALY TWEAK THE ANTENNA MOVEMENT************/...// Allowed error for which antennna won't move. int AzErr =8; int ElErr =4;// Angle difference where soft stop begins int Amax =25; //azimuth int Emax =15; //elevation// min and max power for motors, percents; int PwAzMin =30; //minimum power for which the motor doesn't stall and starts under load int PwAzMax =100; //full power for the fastest speed int PwElMin =30; int PwElMax =100;/****************************************************************************/

Esquemas

Make sure you use this diagram with the code for DC motors.
Connection of all the modules, encoders, LCD, relays, MosFet etc, Make sure you use this diagram with the code for AC motors.
Offers dry contacts (ON/OFF). It can be easily interfaced with commercial rotators.

Processo de manufatura

  1. Luzes de dimerização com PWM usando o botão de ação
  2. Jogo Arduino Gyroscope com MPU-6050
  3. Sensor DHT11 com LEDs e um alto-falante Piezo
  4. Unopad - Controlador Arduino MIDI com Ableton
  5. Homem de Ferro
  6. Sensor de obstáculo simples com Arduino
  7. Encontre-me
  8. Controle do umidificador Arduino
  9. Cubo de LED 4x4x4 com Arduino Uno e 1sheeld
  10. Joystick Arduino