Manufactura industrial
Internet industrial de las cosas | Materiales industriales | Mantenimiento y reparación de equipos | Programación industrial |
home  MfgRobots >> Manufactura industrial >  >> Manufacturing Technology >> Proceso de manufactura

Controlador de rotor de antena compatible con software de seguimiento

Componentes y suministros

Arduino UNO
Placa Arduino Uno
× 1
Potenciómetro giratorio (genérico)
máx. 1Kohm (500 ohmios funcionan mejor)
× 2
Codificador rotatorio con pulsador
× 2
Breadboard (genérico)
× 1
Módulo de relé (genérico)
2 módulos x 2 relés NO-Com-NC
× 2
Canal N del MOSFET de potencia
módulo de energía mosfet (min 12V / 3A)
× 2

Aplicaciones y servicios en línea

Arduino IDE

Acerca de este proyecto

Última actualización noviembre 2021

Este proyecto comenzó como un entretenimiento y se convirtió en un equipo serio.

El controlador acepta el posicionamiento manual de la antena, mediante dos codificadores rotativos, Azimut y Elevación. Puede rastrear satélites automáticamente, cuando se conecta por USB a una PC que ejecuta un software de rastreo de satélites.

Es compatible con todo el software de seguimiento que utiliza el protocolo EasyComm2 / 9600 baudios. PstRotator, WXtrack, HRD, MacDoppler ... Incluso WXtoIMG puede controlar el rotador.

Funciona directamente con Orbitron, con un complemento DDE de http://tripsintech.com/orbitron-dde-azimuth-elevation-to-serial/

El controlador emite una respuesta en serie, para que el software de seguimiento muestre la posición real de la antena en la pantalla. Hasta ahora, solo PstRotator hizo eso.

El código no usa ninguna biblioteca (excepto la pantalla LCD) y se ejecuta exactamente como está, con pines de acuerdo con el diagrama eléctrico a continuación. Si presiona el botón del codificador de azimut, todo movimiento de la antena se detiene inmediatamente y el comando de azimut se puede configurar en 10 grados. pasos.

Aquí encontrará dos versiones:una para motores de CC y otra para motores de CA (solo relés). El último se puede conectar con rotores de antenas comerciales existentes.

La versión con motores de CC tiene la ventaja de utilizar PWM para un movimiento de antena más suave / uniforme. Emite una respuesta de potencia proporcional al error de ángulo (Target <-> Antenna). Por tanto, cuando la antena empieza a moverse, acelera progresivamente y, al acercarse a la posición deseada, ralentiza hasta detenerse por completo. Esto se conoce como Soft-Start / Soft-Stop . Hay una zona muerta ajustable, donde la antena no se mueve para el menor desplazamiento del objetivo.

Tengo una beta versión con Soft-Start / Soft-Stop para motores AC, aprovechando este módulo AC-Dimmer, pero ahora solo funciona para azimut. Si quieres probarlo, avísame por correo electrónico.

Si tiene 180deg. sistema de elevación, estás bien, dame un correo electrónico. También hay una versión con 0.1deg. precisión, pero no lo recomendaría a menos que tenga una lectura de potenciómetro sólida como una roca y un diseño de construcción de controlador paranoico. Encontrarás más versiones en mi página web.

Después de terminar la construcción, debe aplicar los procedimientos de calibración .

  • La calibración del potenciómetro es obligatorio y asegura una lectura correcta de 0-359deg. / 0-90deg., Sin importar qué tipo de potenciómetro esté usando.
  • La calibración del motor es solo para ajustar el Soft-Start-Stop característica. Esto es necesario si no le gusta la configuración predeterminada.

Explicaciones más detalladas en los videos. Debido a que el código se ha mejorado con el tiempo y los videos ya no se pueden actualizar, consulte mi página web para obtener la información más reciente y la experiencia personal al vivir con este controlador. https://racov.ro/index.php/2020/12/09/arduino-based-antenna-rotator-part3-software-tracking-update/

Dame un correo electrónico si quieres saber más, porque esta plataforma no me informa de nuevos comentarios, no sé por qué. Intentaré resolver los pequeños problemas lo mejor que pueda. [email protected]

Muchas gracias a todos los que me enviaron comentarios, ayudando a que este proyecto sea más confiable. Cualquier comentario es muy apreciado.

Código

  • ant-rot-DC-nov2021
  • hormiga-pudrición-AC-aug2021
  • Procedimiento de calibración del potenciómetro
  • Procedimiento de calibración del motor
ant-rot-DC-nov2021 Arduino
Este código es para motores de CC, con salida PWM de arranque-parada suave
 / * AZ / EL Controlador de rotor de antena para Arduino - motores de CC * ========================================================* Utiliza el protocolo EasyComm para computadora - Software de seguimiento * Comando manual por mediante dos codificadores rotativos AZ - EL * * Viorel Racoviteannu * https://www.youtube.com/channel/UCiRLZX0bV9rS04BGAyUf-fA * https://racov.ro * [email protected] * * No puedo asumir ninguna responsabilidad por mal uso de este código * o cualquier tipo de daño que pueda ocurrir por el uso de este código. * * diciembre de 2020 v2:estabilidad mejorada de las comunicaciones en serie * enero de 2021:zona muerta cercana al objetivo mejorada, para la cual la antena no se mueve * abril de 2021:estabilidad mejorada de las comunicaciones en serie * junio de 2021:potencia proporcional de error para el seguimiento del movimiento. Real Soft-Stop * agosto de 2021:actualización USB más rápida, cambio en frío de la dirección Az / El, pequeñas optimizaciones en el código * noviembre de 2021:descifró el secreto de Soft-Start. No fue difícil. Ahí lo tienes * / #include  // Biblioteca para comunicación I2C # incluye  // Biblioteca para LCD // Cableado:el pin SDA está conectado a A4 y el pin SCL a A5.// Conectar a LCD a través de I2C, dirección predeterminada 0x27 (A0-A2 sin puente) LiquidCrystal_I2C lcd (0x27, 16, 2); // dirección, caracteres, filas.// declaración de símbolo personalizado para el byte de flecha arriba / abajo Flecha abajo [8] ={B00000, B00100, B00100, B00100, B10101, B01110, B00100, B00000}; byte UpArrow [8] ={B00000, B00100, B01110, B10101, B00100, B00100, B00100, B00000}; / ************************* ********** ESTE ES DONDE REALMENTE PULSAS EL MOVIMIENTO DE LA ANTENA *************** /// Potenciómetros de ANTENA CALIBRACIÓN int AzMin =1; // inicio del potenciómetro int AzMax =1023; // fin del potenciómetro int ElMin =1; int ElMax =1023; // Error permitido por el cual la antena no se moverá int AzErr =8; int ElErr =4; // Diferencia de ángulo donde comienza la parada suave int Amax =25; // acimut int Emax =15; // elevación // potencia mínima y máxima para motores, porcentajes; int PwAzMin =30; // potencia mínima para la cual el motor no se para y arranca bajo carga int PwAzMax =100; // máxima potencia para la velocidad más rápida int PwElMin =30; int PwElMax =100; int PwAz =0; // potencia calculada a transmitir al motor (porcentajes); int PwEl =0; / ****************************************** *********************************************** ***** /// Variables del codificador enum AzPinAssignments {AzEncoderPinA =2, // Codificador Az derecho AzEncoderPinB =3, // codificador izquierdo AzClearButton =4, // codificador push ElEncoderPinA =6, // El codificador derecho ElEncoderPinB =5 }; // codificador a la izquierda // interrumpir la rutina de servicio vars unsigned int lastReportedPos =1; // gestión de cambios static boolean rotating =false; // gestión antirrebote booleano A_set =false; booleano B_set =falso; int aState; int aLastState; // otras variables int AzPotPin =A0; // seleccione el pin de entrada para el azim. potenciómetro int AzRotPin =12; // seleccione el pin de salida para la dirección de rotación int AzPWMPin =11; // seleccione el pin de salida para el comando azimut PWM int TruAzim =0; // valor de acimut real calculado int ComAzim =0; // valor de acimut ordenado int OldTruAzim =0; // para almacenar el valor de azimut anterior int OldComAzim =0; char AzDir; // símbolo para azim rot display int AzEncBut =1; // variable para alternar con el botón del codificador int ElPotPin =A1; // seleccione el pin de entrada para el elev. potenciómetro int ElRotPin =13; // seleccione el pin de salida para la dirección de rotación de elevación int ElPWMPin =10; // seleccione el pin de salida para el comando PWM de rotación de elevación int TruElev =0; // valor de elevación real calculado int ComElev =0; // valor de elevación ordenado int OldTruElev =0; // para almacenar el valor de elevación anterior int OldComElev =0; char ElDir; // símbolo de elev. rot display // banderas para AZ, tolerancias EL bool AzStop =false; bool ElStop =falso; int ElUp =1; // 1 - Elevación Dn, 0 - Elevación STOP, 2 - Elevación arriba int StaAzim =0; // Arranque Ángulo de acimut para motor Arranque suave int PwAzStop =0; // PWM calculado (porcentaje) para parada suave int PwAzStar =0; // PWM calculado (porcentaje) para arranque suave int StaElev =0; // Arranque Ángulo de elevación para motor Arranque suave int PwElStop =0; // PWM calculado (porcentaje) para parada suave int PwElStar =0; // PWM calculado (porcentaje) para inicio suave // ​​ciclo de promediado const int numReadings =25; int readIndex =0; // el índice de la lectura actual int azimuth [numReadings]; // las lecturas de la entrada analógica int elevación [numReadings]; int totalAz =0; // el total acumulado int totalEl =0; // variables para comunicación serial String Azimuth =""; Elevación de cadena =""; String ComputerRead; String ComputerWrite; bool AZser =falso; bool ELser =falso; bool ANTser =false; / *************** DECLARACIÓN DE VARIABLE FINAL ************ / void setup () {Serial.begin (9600); Serial.setTimeout (50); // milisegundos para esperar USB sata. Predeterminado 1000 // Iniciar la pantalla LCD:// lcd.begin (16,2); // seleccione este si las flechas no se muestran correctamente lcd.init (); lcd.backlight (); // escribir en el nombre para mostrar y la versión lcd.setCursor (0, 0); // Coloque el cursor en la primera fila de la primera columna (¡el conteo comienza en 0!) Lcd.print ("EasyCom AntRotor"); // muestra "..." lcd.setCursor (0, 1); // Coloque el cursor en la primera columna de la segunda fila lcd.print ("* Racov * Nov.2021"); // creando un símbolo personalizado para la flecha hacia arriba / abajo lcd.createChar (1, DownArrow); lcd.createChar (2, flecha arriba); // declaración de pin pinMode (AzRotPin, OUTPUT); // declarando azim. dirección de rotación Pin como OUTPUT pinMode (AzPWMPin, OUTPUT); // declarando el Pin de comando azimut PWM como OUTPUT pinMode (ElRotPin, OUTPUT); // declarando elev. dirección de rotación Pin como OUTPUT pinMode (ElPWMPin, OUTPUT); pinMode (AzPotPin, ENTRADA); pinMode (ElPotPin, ENTRADA); pinMode (AzEncoderPinA, ENTRADA); pinMode (AzEncoderPinB, ENTRADA); pinMode (AzClearButton, ENTRADA); pinMode (ElEncoderPinA, ENTRADA); pinMode (ElEncoderPinB, INPUT); // Pin AzEncoder en interrupción 0 (pin A) attachInterrupt (0, doEncoderA, CHANGE); // Pin AzEncoder en interrupción 1 (pin B) attachInterrupt (1, doEncoderB, CHANGE); // Lee el estado inicial de ElEncoderPinA aLastState =digitalRead (ElEncoderPinA); / * inicialización del bucle de promediado * / TruAzim =(map (analogRead (AzPotPin), AzMin, AzMax, 0, 359)); // valor de acimut 0-359 if (TruAzim <0) {TruAzim =0;} if (TruAzim> 359) {TruAzim =359;} // mantener los valores entre los límites TruElev =(map (analogRead (ElPotPin), ElMin, ElMax , 0, 90)); // valor elev 0-90 if (TruElev <0) {TruElev =0;} if (TruElev> 90) {TruElev =90;} // mantener los valores entre los límites para (int thisReading =0; thisReading  359) {TruAzim =359;} if (TruElev <0) {TruElev =0;} if (TruElev> 90) {TruElev =90;} // avanza a la siguiente posición en la matriz:readIndex =readIndex + 1; // si estamos al final de la matriz, regrese al principio:if (readIndex> =numReadings) {readIndex =0;} // esto es para leer el comando del codificador ReadAzimEncoder (); ReadElevEncoder (); if (Serial.available ()) {SerComm ();} // leer datos USB // actualizar la visualización de la posición de la antena solo si el valor cambia if ((millis ()% 500) <10) {// no hacer parpadear la pantalla si (OldTruAzim! =TruAzim) {DisplAzim (TruAzim, 4,0); OldTruAzim =TruAzim; } si (OldTruElev! =TruElev) {DisplElev (TruElev, 5,1); OldTruElev =TruElev; }} // actualiza la visualización de la posición de destino solo si el valor cambia if (OldComAzim! =ComAzim) {DisplAzim (ComAzim, 12,0); OldComAzim =ComAzim; } if (OldComElev! =ComElev) {DisplElev (ComElev, 13,1); OldComElev =ComElev; } // esto es para rotar en azimut if (TruAzim ==ComAzim) {// si es igual, deja de moverse AzStop =true; analogWrite (AzPWMPin, 0); // Potencia del motor Az =0 StaAzim =TruAzim; // este será el acimut de inicio para el inicio suave lcd.setCursor (8, 0); lcd.print ("="); } else if ((abs (TruAzim - ComAzim) <=AzErr) &&(AzStop ==false)) {// si está en tolerancia, pero no era igual, rotar AzimRotate ();} else if (abs (TruAzim - ComAzim)> AzErr) {// si el objetivo está fuera de tolerancia AzStop =false; // no es igual AzimRotate (); // rotar} // esto es rotar en elevación if (TruElev ==ComElev) {// si es igual, dejar de mover ElStop =true; analogWrite (ElPWMPin, 0); // El motor power =0 StaElev =TruElev; // esta será la elevación inicial para el inicio suave lcd.setCursor (8, 1); lcd.print ("="); ElUp =0; // marca para parada de elevación} else if ((abs (TruElev - ComElev) <=ElErr) &&(ElStop ==false)) {// si está en tolerancia, pero no era igual, rotar ElevRotate ();} else if (abs (TruElev - ComElev)> ElErr) {// si el objetivo está fuera de tolerancia ElStop =false; // no es igual a ElevRotate (); // rotar} // esto es para interpretar la multiplicación x10 del codificador Az while (AzEncBut ==10) {// mientras se cambia a x10 analogWrite (AzPWMPin, 0); // DETENGA la rotación de la antena StaAzim =TruAzim; // este será el acimut de inicio para analogWrite de inicio suave (ElPWMPin, 0); lcd.setCursor (8, 0); lcd.print ("*"); ReadAzimEncoder (); if (OldComAzim! =ComAzim) {// actualiza la pantalla solo si los números cambian DisplAzim (ComAzim, 12, 0); OldComAzim =ComAzim; } retraso (100); }} // finaliza el LOOP principal // ____________________________________________________ // ___________procedimientosdefiniciones__________________void DisplAzim (int x, int y, int z) {char displayString [7] =""; sprintf (displayString, "% 03d", x); // genera un número de longitud fija (3 enteros) lcd.setCursor (y, z); // sin ceros a la izquierda "__7" use "% 3d" lcd.print (displayString); // ************** CON FINES DE CALIBRACIÓN ************** // Serial.print ("Az"); // Serial.println ( analogRead (AzPotPin));} void DisplElev (int x, int y, int z) {char displayString [7] =""; sprintf (displayString, "% 02d", x); // genera un número de longitud fija (2 enteros) lcd.setCursor (y, z); // sin ceros a la izquierda "_7" use "% 2d" lcd.print (displayString); // ************** PARA FINES DE CALIBRACIÓN ********** **** // Serial.print ("El"); // Serial.println (analogRead (ElPotPin));} void ReadElevEncoder () {aState =digitalRead (ElEncoderPinA); // Lee el estado "actual" de ElEncoderPinA // Si el estado anterior y actual de ElEncoderPinA son diferentes, eso significa que ha ocurrido un Pulso if (aState! =ALastState) {// Si el estado de ElEncoderPinB es diferente al ElEncoderPinA, eso significa que el codificador está girando en el sentido de las agujas del reloj if (digitalRead (ElEncoderPinB)! =AState) {ComElev ++;} else {ComElev -;} if (ComElev <0) {ComElev =0;} if (ComElev> 90 ) {ComElev =90;}} aLastState =aState; // Actualiza el estado anterior de ElEncoderPinA con el estado actual} void ReadAzimEncoder () {rotating =true; // reinicia el antirrebote if (lastReportedPos! =ComAzim) {lastReportedPos =ComAzim; } retraso (10); if (digitalRead (AzClearButton) ==LOW) {// si el interruptor del codificador está presionado delay (250); // interruptor antirrebote if (AzEncBut ==1) {AzEncBut =10; ComAzim =int (ComAzim / 10) * 10; // ComAzim en 10deg. pasos} else {AzEncBut =1; }}} // finaliza ReadAzimEncoder () // Interrumpe en un cambio de estadovoid doEncoderA () {// debounce if (rotating) delay (1); // Espere un poco hasta que termine el rebote // Probar la transición, ¿realmente cambiaron las cosas? if (digitalRead (AzEncoderPinA)! =A_set) {// antirrebote una vez más A_set =! A_set; // ajustar el contador + si A adelanta a B if (A_set &&! B_set) ComAzim + =AzEncBut; ComAzim =((ComAzim + 360)% 360); // encoderPos entre 0 y 359 grados. rotatorio =falso; // no más antirrebote hasta que loop () golpee de nuevo}} // Interrumpa en B cambiando de estado, igual que A abovevoid doEncoderB () {if (rotating) delay (1); if (digitalRead (AzEncoderPinB)! =B_set) {B_set =! B_set; // ajusta el contador - 1 si B adelanta a A if (B_set &&! A_set) ComAzim - =AzEncBut; ComAzim =((ComAzim + 360)% 360); // encoderPos entre 0 y 359 grados. rotatorio =falso; }} void AzimRotate () {if (ComAzim> TruAzim) {// esto para determinar la dirección de rotación // conmutación en frío - detener el motor antes de cambiar de dirección - para proteger las partes mecánicas y eléctricas if (AzDir ==char (127)) { // si anteriormente giraba en la dirección opuesta analogWrite (AzPWMPin, 0); // DETENGA el motor StaAzim =TruAzim; // este será el acimut de inicio para el retardo de inicio suave (200); // retardo previo al cambio digitalWrite (AzRotPin, LOW); // desactivar el pin de rotación - girar a la derecha delay (200); // retardo posterior al cambio} else {// mismo directin, sin parada, sin retardo digitalWrite (AzRotPin, LOW); // desactivar el pin de rotación - rotar a la derecha} AzDir =char (126); // "->"} else {if (AzDir ==char (126)) {// si anteriormente giraba en la dirección opuesta analogWrite (AzPWMPin, 0); // DETENGA el motor StaAzim =TruAzim; // este será el acimut de inicio para el retardo de inicio suave (200); // retardo previo al cambio digitalWrite (AzRotPin, HIGH); // activa el pin de rotación - rota a la izquierda delay (200); // retardo posterior al cambio} else {// mismo directin, sin parada, sin retardo digitalWrite (AzRotPin, HIGH); // activar el pin de rotación - rotar a la izquierda} AzDir =char (127); // "<-"} lcd.setCursor (8, 0); lcd.print (Cadena (AzDir)); // esto activa el pin azim PWM proporcional al error de ángulo (calculado en porcentajes%) PwAzStop =PwAzMin + round ((abs (ComAzim-TruAzim)) * (PwAzMax-PwAzMin) / Amax); // fórmula que genera una potencia proporcional con la diferencia de ángulo para Soft-Stop PwAzStar =PwAzMin + round ((abs (StaAzim-TruAzim)) * (PwAzMax-PwAzMin) / Amax); // fórmula que genera una potencia proporcional a la diferencia de ángulo para Soft-Start if (PwAzStar> PwAzStop) {PwAz =PwAzStop; // elija el valor que sea más pequeño} else {PwAz =PwAzStar;} if (PwAz> PwAzMax) {PwAz =PwAzMax;} analogWrite (AzPWMPin, round (2.55 * PwAz)); // activa el pin PWM de Azim drive} // finaliza AzimRotate () void ElevRotate () {// esto para determinar la dirección de rotación if (ComElev> TruElev) {if (ElUp ==1) {// si previamente rotando en el opuesto dirección analogWrite (ElPWMPin, 0); // DETENGA el motor StaElev =TruElev; // esta será la elevación de inicio para el retardo de inicio suave (200); // retardo previo al cambio digitalWrite (ElRotPin, LOW); // desactivar el pin de rotación - rotar el retardo ARRIBA (200); // retardo posterior al cambio} else {// mismo directin, sin parada, sin retardo digitalWrite (ElRotPin, LOW); // desactivar el pin de rotación - girar ARRIBA} lcd.setCursor (8, 1); lcd.write (2); // flecha hacia arriba ElUp =2; // marca para elevación ARRIBA} else {if (ElUp ==2) {// si anteriormente giraba en la dirección opuesta analogWrite (ElPWMPin, 0); // DETENGA el motor StaElev =TruElev; // esta será la elevación de inicio para el retardo de inicio suave (200); // retardo previo al cambio digitalWrite (ElRotPin, HIGH); // desactivar el pin de rotación - rotar el retardo ARRIBA (200); // retardo posterior al cambio} else {// mismo directin, sin parada, sin retardo digitalWrite (ElRotPin, HIGH); // desactivar el pin de rotación - girar ARRIBA} lcd.setCursor (8, 1); lcd.write (1); // flecha hacia abajo ElUp =1; // marca para elevación DN} // esto activa el pin azim PWM proporcional al error de ángulo (calculado en porcentajes%) PwElStop =PwElMin + round ((abs (ComElev-TruElev)) * (PwElMax-PwElMin) / Emax); // fórmula que genera una potencia proporcional con la diferencia de ángulo para Soft-Stop PwElStar =PwElMin + round ((abs (StaElev-TruElev)) * (PwElMax-PwElMin) / Emax); // fórmula que genera una potencia proporcional a la diferencia de ángulo para Soft-Start if (PwElStar> PwElStop) {PwEl =PwElStop; // elija el valor que sea más pequeño} else {PwEl =PwElStar;} if (PwEl> PwElMax) {PwEl =PwElMax;} analogWrite (ElPWMPin, round (2.55 * PwEl)); // activa el pin PWM de la unidad Elev} // finaliza ElevRotate () void SerComm () {// inicializa las lecturas ComputerRead =""; Azimut =""; Elevación =""; while (Serial.available ()) {ComputerRead =Serial.readString (); // lee los datos entrantes como una cadena // Serial.println (ComputerRead); // repite la recepción con fines de prueba} // buscando el comando  para (int i =0; i <=ComputerRead.length (); i ++) {if ((ComputerRead.charAt (i) ==' A ') &&(ComputerRead.charAt (i + 1) ==' Z ')) {// si se lee AZ para (int j =i + 2; j <=ComputerRead.length (); j ++) {if (isDigit (ComputerRead.charAt (j))) {// si el carácter es un número Azimut =Azimut + ComputerRead.charAt (j); } else {break;}}}} // buscando el comando  para (int i =0; i <=(ComputerRead.length () - 2); i ++) {if ((ComputerRead.charAt (i ) =='E') &&(ComputerRead.charAt (i + 1) =='L')) {// si se lee EL if ((ComputerRead.charAt (i + 2)) =='-') {ComElev =0; // si la elevación es negativa, se rompe; } for (int j =i + 2; j <=ComputerRead.length (); j ++) {if (isDigit (ComputerRead.charAt (j))) {// si el carácter es número Elevation =Elevation + ComputerRead.charAt ( j); } else {break;}}}} // si  recibió if (Azimuth! ="") {ComAzim =Azimuth.toInt (); ComAzim =ComAzim% 360; // manteniendo los valores entre los límites (para seguidores con más de 360 ​​grados de rotación)} // si  recibió if (Elevation! ="") {ComElev =Elevation.toInt (); if (ComElev> 180) {ComElev =0;} if (ComElev> 90) {// si recibió más de 90deg. (para seguidores con elevación de 180 grados) ComElev =180-ComElev; // manténgase por debajo de los 90 grados. ComAzim =(ComAzim + 180)% 360; // y gire la antena en la parte posterior}} // buscando la interogación  para la posición de la 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')) {// devuelve la posición de la antena <+ xxx.x xx.x> ComputerWrite ="+" + String (TruAzim) + ". 0" + String (TruElev) + ". 0 "; Serial.println (ComputerWrite); }}} // finaliza SerComm () 
ant-rot-AC-aug2021 Arduino
Asegúrese de utilizar el diagrama eléctrico para motores de CA
Ofrece contactos secos (ON / OFF). Se puede conectar fácilmente con rotadores comerciales.
 / * Controlador de rotor de antena AZ / EL para Arduino - motores de CA * ========================================================* Utiliza el protocolo EasyComm para computadora - Software de seguimiento * Comando manual mediante dos codificadores rotativos AZ - EL * * compatible con rotadores de caja de interruptores * o motores de CA * contactos secos para izquierda-derecha, arriba-abajo * * Viorel Racoviteannu / * https://www.youtube.com/channel/UCiRLZX0bV9rS04BGAyUf-fA * https://racov.ro * [email protected] * * No puedo asumir ninguna responsabilidad por el mal uso de este código * o cualquier tipo de daño que pueda ocurrir por el uso de este código. * * diciembre de 2020 v2:estabilidad de comunicación en serie mejorada * enero de 2021:AZ fija, tolerancias EL para la activación del motor * abril de 2021:estabilidad de comunicación en serie mejorada * agosto de 2021:actualización USB más rápida, conmutación en frío dirección Az / El, pequeñas optimizaciones en el código * / #include  // Biblioteca para comunicación I2C # incluye  // https://www.arduinolibraries.info/libraries/liquid-crystal-i2-c (Biblioteca para LCD) // Cableado:el pin SDA está conectado a A4 y el pin SCL a A5.// Conectar a LCD a través de I2C, dirección predeterminada 0x27 (A0-A2 no puenteado) LiquidCrystal_I2C lcd (0x27, 16, 2); // dirección, caracteres, filas.// declaración de símbolo personalizado para el byte de flecha arriba / abajo Flecha abajo [8] ={B00000, B00100, B00100, B00100, B10101, B01110, B00100, B00000}; byte UpArrow [8] ={B00000, B00100, B01110, B10101, B00100, B00100, B00100, B00000}; // CALIBRACIÓN potenciómetros ANTENA int AzMin =1; // inicio del potenciómetro int AzMax =1023; // fin del potenciómetro int ElMin =1; int ElMax =1023; // Error permitido por el cual la antena no se mueve int AzErr =8; int ElErr =4;// Azim encoder variables enum AzPinAssignments { AzEncoderPinA =2, // encoder right AzEncoderPinB =3, // encoder left AzClearButton =4}; // encoder push unsigned int lastReportedPos =1; // change management static boolean rotating =false; // debounce management // interrupt service routine vars boolean A_set =false; boolean B_set =false; //Elev encoder variables enum ElPinAssignments{ ElEncoderPinA =6, // encoder right ElEncoderPinB =5, // encoder left ElClearButton =7}; // encoder push int aState; int aLastState; // other variables int AzPotPin =A0; // select the input pin for the azim. potentiometer int AzRotPinR =13; // select the out pin for rotation direction int AzRotPinL =12; int TruAzim =0; // calculated real azimuth value int ComAzim =0; // commanded azimuth value int OldTruAzim =0; // to store previous azimuth value int OldComAzim =0; char AzDir; // symbol for azim rot display int AzEncBut =1; // variable to toggle with encoder push button int ElPotPin =A1; // select the input pin for the elev. potentiometer int ElRotPinD =11; // select the out pin for elevation rotation direction int ElRotPinU =10; int TruElev =0; // calculated real elevation value int ComElev =0; // commanded elevation value int OldTruElev =0; // to store previous elevation value int OldComElev =0; char ElDir; // symbol for elev. rot display int ElEncBut =1; // variable to toggle with encoder push button // flags for AZ, EL tolerances bool AzStop =false; bool ElStop =false; int ElUp =0; // 1 =Elevation Dn, 0 =Elevation STOP, 2 =Elevation Up //averaging loop const int numReadings =25; int readIndex =0; // the index of the current reading int azimuth[numReadings]; // the readings from the analog input int elevation[numReadings]; int totalAz =0; // the running total int totalEl =0;// variables for serial comm String Azimuth =""; String Elevation =""; String ComputerRead; String ComputerWrite; bool AZser =false; bool ELser =false; bool ANTser =false;/*************** END VARIABLE DECLARATION ************/ void setup() { Serial.begin(9600); Serial.setTimeout(50); // miliseconds to wait for USB sata. Default 1000// Initiate the LCD:// lcd.begin(16,2); //select this one if the arrows are not displayed correctly lcd.init(); lcd.backlight();// write on display name and version lcd.setCursor(0, 0); // Set the cursor on the first column first row.(counting starts at 0!) lcd.print("EasyCom AntRotor"); lcd.setCursor (0, 1); // Set the cursor on the first column the second row lcd.print("*Racov* Aug.2021 ");//creating custom symbol for up/dwn arrow lcd.createChar(1, DownArrow); lcd.createChar(2, UpArrow); // pin declaration pinMode(AzRotPinR, OUTPUT); //declaring azim. rotation direction Pin as OUTPUT pinMode(AzRotPinL, OUTPUT); pinMode(ElRotPinD, OUTPUT); //declaring elev. rotation direction Pin as 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);// AzEncoder pin on interrupt 0 (pin A) attachInterrupt(0, doEncoderA, CHANGE);// AzEncoder pin on interrupt 1 (pin B) attachInterrupt(1, doEncoderB, CHANGE);// Reads the initial state of the ElEncoderPinA aLastState =digitalRead(ElEncoderPinA);/* initialization of the averaging loop */ TruAzim =(map(analogRead(AzPotPin), AzMin, AzMax, 0, 359)); // azimuth value 0-359 if (TruAzim<0) {TruAzim=0;} if (TruAzim>359) {TruAzim=359;} // keep values between limits TruElev =(map(analogRead(ElPotPin), ElMin, ElMax, 0, 90)); // elev value 0-90 if (TruElev<0) {TruElev=0;} if (TruElev>90) {TruElev=90;} // keep values between limits for (int thisReading =0; thisReading 359) {TruAzim=359;} if (TruElev<0) {TruElev=0;} if (TruElev>90) {TruElev=90;} // advance to the next position in the array:readIndex =readIndex + 1; // if we're at the end of the array, wrap around to the beginning:if (readIndex>=numReadings) {readIndex =0;} // this is to read the command from encoder ReadAzimEncoder(); ReadElevEncoder(); if (Serial.available()) {SerComm();} // read USB data// update antenna position display only if value change if ((millis()%500)<10){ //not to flicker the display if (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; } delay(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; } retraso (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.

Proceso de manufactura

  1. Atenuación de luces con PWM usando el botón pulsador
  2. Juego de giroscopio Arduino con MPU-6050
  3. Sensor DHT11 con LED y altavoz piezoeléctrico
  4. Unopad - Controlador MIDI Arduino con Ableton
  5. Iron Man
  6. Sensor de obstáculos simple con Arduino
  7. Encuéntrame
  8. Control del humidificador Arduino
  9. Cubo LED 4x4x4 con Arduino Uno y 1sheeld
  10. Joystick de Arduino
  11. Podómetro (Arduino 101)