亿迅智能制造网
工业4.0先进制造技术信息网站!
首页 | 制造技术 | 制造设备 | 工业物联网 | 工业材料 | 设备保养维修 | 工业编程 |
home  MfgRobots >> 亿迅智能制造网 >  >> Manufacturing Technology >> 制造工艺

与跟踪软件兼容的天线旋转器控制器

组件和用品

Arduino UNO
Arduino Uno 板
× 1
旋转电位器(通用)
最大。 1Kohm(500 欧姆效果更好)
× 2
带按钮的旋转编码器
× 2
面包板(通用)
× 1
中继模块(通用)
2 个模块 x 2 个继电器 NO-Com-NC
× 2
功率 MOSFET N 沟道
功率 mosfet 模块(最低 12V/3A)
× 2

应用和在线服务

Arduino IDE

关于这个项目

最新更新 11 月 2021

这个项目开始是一个娱乐项目,后来变成了一个严肃的设备。

控制器接受天线的手动定位,通过两个旋转编码器,方位角和仰角。当通过USB连接到运行卫星跟踪软件的PC时,它可以自动跟踪卫星。

它与所有使用 EasyComm2 协议/9600 波特的跟踪软件兼容。 PstRotator、WXtrack、HRD、MacDoppler……甚至 WXtoIMG 都可以控制旋转器。

它直接与 Orbitron 配合使用,带有来自 http://tripsintech.com/orbitron-dde-azimuth-elevation-to-serial/

的 DDE 插件

控制器输出串行响应,以便跟踪软件在屏幕上显示实际天线位置。到目前为止,只有 PstRotator 做到了这一点。

该代码不使用任何库(LCD 除外)并完全按原样运行,根据下面的电气图使用引脚。如果按下方位角编码器的按钮,所有天线运动立即停止,方位角命令可以设置为 10 度。步骤。

您会在这里找到两种版本:一种用于直流电机,一种用于交流电机(仅继电器)。后者可以与现有的商用天线旋转器接口。

直流电机版本的优点是使用 PWM 来实现更柔和/更平滑的天线运动。它输出与角度误差成正比的功率响应(目标<->天线)。因此,当天线开始移动时,它会逐渐加速,当接近所需位置时,它会减速直至完全停止。这称为软启动/软停止 .有一个可调节的死区,天线不会因为最轻微的目标偏移而移动。

我有一个测试版 带有软启动/软停止的版本 对于交流电机,利用这个 AC-Dimmer 模块,但现在它只适用于方位角。如果您想尝试一下,请通过电子邮件告诉我。

如果你有180度。高程系统,你很好,给我一个电子邮件。还有一个0.1度的版本。精度,但我不会推荐它,除非你有一个该死的坚如磐石的电位计读数和一个偏执的控制器结构设计。您会在我的网页上找到更多版本。

完成施工后,您必须应用校准程序 .

  • 电位器校准 是强制性的,并确保 0-359 度的正确读数。 / 0-90deg.,无论您使用哪种电位器。
  • 电机校准 仅用于调整软启动-停止 特征。如果您不喜欢默认设置,这很有必要。

视频中有更详细的解释。由于代码已经随着时间的推移而改进,并且视频无法再更新,请查看我的网页以获取最新信息和使用此控制器的个人体验。 https://racov.ro/index.php/2020/12/09/arduino-based-antenna-rotator-part3-software-tracking-update/

如果你想知道更多,给我一个电子邮件,因为这个平台不会通知我新的评论,不知道为什么。我会尽力解决小问题。 [email protected]

非常感谢所有向我发送反馈的人,他们帮助使这个项目更加可靠。任何反馈都受到高度赞赏。

代码

  • ant-rot-DC-nov2021
  • ant-rot-AC-aug2021
  • 电位器校准程序
  • 电机校准程序
ant-rot-DC-nov2021Arduino
此代码适用于直流电机,具有软启动-停止 PWM 输出
/* 用于 Arduino 的 AZ/EL 天线旋转器控制器 - 直流电机 * ========================================================* 计算机使用 EasyComm 协议 - 跟踪软件 * 手动命令由两个旋转编码器 AZ - EL * * Viorel Racoviteannu * https://www.youtube.com/channel/UCiRLZX0bV9rS04BGAyUf-fA * https://racov.ro * [email protected] * * 我不承担任何责任滥用此代码 * 或使用此代码可能造成的任何类型的损坏。 * * 2020 年 12 月 v2 - 改进了串行通信稳定性 * 2021 年 1 月 - 改进了近目标死区,为此天线不会移动 * 2021 年 4 月 - 改进串行通信稳定性 * 2021 年 6 月 - 跟踪移动的误差比例功率。真正的软停止 * 2021 年 8 月 - 更快的 USB 更新,冷切换 Az/El 方向,代码中的小优化 * 2021 年 11 月 - 破解了软启动的秘密。这并不难。你有它 */ #include  // I2C 通信库#include  // LCD 库// 接线:SDA 引脚连接到 A4,SCL 引脚连接到 A5。// 连接通过 I2C 到 LCD,默认地址 0x27(A0-A2 不跳线)LiquidCrystal_I2C lcd(0x27, 16, 2); // 地址、字符、行。// 为向上/向下箭头字节声明自定义符号 DownArrow[8] ={ B00000, B00100, B00100, B00100, B10101, B01110, B00100, B00000}; byte UpArrow[8] ={ B00000, B00100, B01110, B10101, B00100, B00100, B00100, B00000};/************************* **********这是您真正调整天线运动的地方***************//// 天线电位计校准 int AzMin =1; //开始电位器 int AzMax =1023; //电位器结束 int ElMin =1; int ElMax =1023;// 允许的错误导致天线不会移动 int AzErr =8; int ElErr =4;// 软停止开始的角度差 int Amax =25; //方位角int Emax =15; //海拔//电机的最小和最大功率,百分比; int PwAzMin =30; //电机在负载下不失速和启动的最小功率 int PwAzMax =100; //全功率最快速度 int PwElMin =30; int PwElMax =100; int PwAz =0; //计算的要传输到电机的功率(百分比); int PwEl =0;/************************************************ ****************************************************** *****/// 编码器变量 enum AzPinAssignments { AzEncoderPinA =2, // 编码器右 AzEncoderPinB =3, // 编码器左 AzClearButton =4, // 编码器推 ElEncoderPinA =6, // El 编码器右 ElEncoderPinB =5 }; // 左编码器 // 中断服务程序 vars unsigned int lastReportedPos =1; // 更改管理 static boolean rotation =false; // 去抖动管理 boolean A_set =false;布尔 B_set =false;在一个国家; int aLastState; // 其他变量 int AzPotPin =A0; // 选择方位角的输入引脚。电位器 int AzRotPin =12; // 选择旋转方向的输出引脚 int AzPWMPin =11; // 为方位角 PWM 命令选择输出引脚 int TruAzim =0; // 计算出的真实方位角值 int ComAzim =0; // 指令方位角值 int OldTruAzim =0; // 存储先前的方位角值 int OldComAzim =0;字符 AzDir; // 方位角旋转显示符号 int AzEncBut =1; // 用编码器按钮切换的变量 int ElPotPin =A1; // 选择电梯的输入引脚。电位器 int ElRotPin =13; // 选择仰角旋转方向的输出引脚 int ElPWMPin =10; // 选择用于仰角旋转 PWM 命令的输出引脚 int TruElev =0; // 计算出的实际高程值 int ComElev =0; // 指令海拔值 int OldTruElev =0; // 存储之前的海拔值 int OldComElev =0;字符 ElDir; // 海拔符号。 rot display // AZ 标志,EL 容差 bool AzStop =false; bool ElStop =false; int ElUp =1; // 1 - Elevation Dn, 0 - Elevation STOP, 2 - Elevation Up int StaAzim =0; // 电机启动方位角软启动 int PwAzStop =0; // 计算软停止的 PWM(百分比) int PwAzStar =0; // 计算软启动的 PWM(百分比) int StaElev =0; // 电机启动仰角软启动 int PwElStop =0; // 计算软停止的 PWM(百分比) int PwElStar =0; // 计算软启动的 PWM(百分比) //平均循环 const int numReadings =25; int readIndex =0; // 当前读数的索引 int azimuth[numReadings]; // 来自模拟输入的读数 int 海拔 [numReadings]; int totalAz =0; // 运行总数 int totalEl =0;// 串行通信变量 String Azimuth ="";字符串高度 ="";字符串 ComputerRead;字符串 ComputerWrite; bool AZser =假;布尔 ELser =假; bool ANTser =false;/**************** 结束变量声明 ************/void setup() { Serial.begin(9600); Serial.setTimeout(50); // 等待 USB 数据的毫秒数。 Default 1000 // 启动 LCD:// lcd.begin(16,2); //如果箭头未正确显示,请选择此项 lcd.init(); lcd.backlight();// 写入显示名称和版本 lcd.setCursor(0, 0); // 将光标设置在第一列第一行。(从 0 开始计数!) lcd.print("EasyCom AntRotor"); // 显示 "..." lcd.setCursor(0, 1); // 将光标设置在第一列第二行 lcd.print("*Racov* Nov.2021");//为向上/向下箭头创建自定义符号 lcd.createChar(1, DownArrow); lcd.createChar(2, UpArrow); // 引脚声明 pinMode(AzRotPin, OUTPUT); //声明方位。旋转方向 Pin 作为 OUTPUT pinMode(AzPWMPin, OUTPUT); //将方位角PWM命令Pin声明为OUTPUT pinMode(ElRotPin, OUTPUT); //声明高。旋转方向 Pin 作为 OUTPUT pinMode(ElPWMPin, OUTPUT); pinMode(AzPotPin,输入); pinMode(ElPotPin,输入); pinMode(AzEncoderPinA,输入); pinMode(AzEncoderPinB,输入); pinMode(AzClearButton, INPUT); pinMode(ElEncoderPinA,输入); pinMode(ElEncoderPinB, INPUT);// 中断 0 上的 AzEncoder 引脚(引脚 A) attachInterrupt(0, doEncoderA, CHANGE);// 中断 1 上的 AzEncoder 引脚(引脚 B) attachInterrupt(1, doEncoderB, CHANGE);// 读取ElEncoderPinA 的初始状态 aLastState =digitalRead(ElEncoderPinA);/* 平均循环的初始化 */ TruAzim =(map(analogRead(AzPotPin), AzMin, AzMax, 0, 359)); // 方位角值 0-359 if (TruAzim<0) {TruAzim=0;} if (TruAzim>359) {TruAzim=359;} // 保持值在限制之间 TruElev =(map(analogRead(ElPotPin), ElMin, ElMax) , 0, 90)); // elev 值 0-90 if (TruElev<0) {TruElev=0;} if (TruElev>90​​) {TruElev=90;} // 将值保持在限制之间 for (int thisReading =0; thisReading 359) {TruAzim=359;} if (TruElev<0) {TruElev=0;} if (TruElev>90​​) {TruElev =90;} // 前进到数组中的下一个位置:readIndex =readIndex + 1; // 如果我们在数组的末尾,则返回到开头: if (readIndex>=numReadings) {readIndex =0;} // 这是从编码器读取命令 ReadAzimEncoder(); ReadElevEncoder(); if (Serial.available()) {SerComm();} // 读取 USB 数据 // 仅当值改变时更新天线位置显示 if ((millis()%500)<10){ // 不闪烁显示 if (OldTruAzim!=TruAzim) { DisplAzim(TruAzim,4,0); OldTruAzim =TruAzim; } if (OldTruElev!=TruElev) { DisplElev(TruElev,5,1); OldTruElev =TrueElev; } }// 仅当值改变时更新目标位置显示 if (OldComAzim !=ComAzim) { DisplAzim(ComAzim,12,0); OldComAzim =ComAzim; } if (OldComElev !=ComElev) { DisplElev(ComElev,13,1); OldComElev =ComElev; }// 这是方位角旋转 if (TruAzim ==ComAzim) { // 如果相等,停止移动 AzStop =true;模拟写入(AzPWMPin,0); // Az 电机功率 =0 StaAzim =TruAzim; // 这将是软启动的起始方位角 lcd.setCursor(8, 0);液晶显示(“=”); } else if ((abs(TruAzim - ComAzim)<=AzErr)&&(AzStop ==false)) { // 如果在容差范围内,但不相等,则旋转 AzimRotate();} else if (abs(TruAzim) - ComAzim)>AzErr){ // 如果目标超出容差 AzStop =false; // 它不等于 AzimRotate(); // 旋转 }// 这是在仰角旋转 if (TruElev ==ComElev) { // 如果相等,停止移动 ElStop =true;模拟写入(ElPWMPin,0); // El 电机功率 =0 StaElev =TrueElev; // 这将是软启动的起始高度 lcd.setCursor(8, 1);液晶显示(“=”); ElUp =0; // 标高 STOP } else if ((abs(TruElev - ComElev)<=ElErr)&&(ElStop ==false)) { // 如果在容差范围内,但不相等,则旋转 ElevRotate();} else if (abs(TruElev - ComElev)>ElErr){ // 如果目标超出容差 ElStop =false; // 它不等于 ElevRotate(); // 旋转 } // 这是解释 Az 编码器 x10 乘法 while (AzEncBut ==10) { // while 切换到 x10 analogWrite(AzPWMPin, 0); // 停止天线旋转 StaAzim =TruAzim; // 这将是软启动的起始方位analogWrite(ElPWMPin, 0); lcd.setCursor(8, 0);液晶打印(“*”); ReadAzimEncoder(); if (OldComAzim !=ComAzim){ // 仅当数字改变时才更新显示 DisplAzim(ComAzim, 12, 0); OldComAzim =ComAzim; } 延迟(100); }}// 结束主循环//______________________________________________________// ___________过程定义__________________void DisplAzim(int x, int y, int z) { char displayString[7] =""; sprintf(displayString, "%03d", x); //输出一个固定长度的数字(3个整数) lcd.setCursor(y, z); // 对于没有前导零 "__7" 使用 "%3d" lcd.print(displayString); // ************** 用于校准目的 **************// Serial.print ("Az ");// Serial.println ( AnalogRead(AzPotPin));}void DisplElev(int x, int y, int z){ char displayString[7] =""; sprintf(displayString, "%02d", x); //输出一个固定长度的数字(2个整数) lcd.setCursor(y, z); // 对于没有前导零“_7”,使用“%2d”lcd.print(displayString);// ************** 用于校准目的 ********** ****// Serial.print ("El ");// Serial.println (analogRead(ElPotPin));}void ReadElevEncoder() { aState =digitalRead(ElEncoderPinA); // 读取 ElEncoderPinA 的“当前”状态 // 如果 ElEncoderPinA 的前一个状态和当前状态不同,则表示发生了 Pulse if (aState !=aLastState){ // 如果 ElEncoderPinB 状态与当前状态不同ElEncoderPinA 状态,表示编码器顺时针旋转 if (digitalRead(ElEncoderPinB) !=aState) { ComElev ++;} else { ComElev --;} if (ComElev <0) {ComElev =0;} if (ComElev>90 ) {ComElev =90;} } aLastState =aState; // 用当前状态更新 ElEncoderPinA 的先前状态}void ReadAzimEncoder() { rotation =true; // 重置去抖动器 if (lastReportedPos !=ComAzim) { lastReportedPos =ComAzim; } 延迟(10); if (digitalRead(AzClearButton) ==LOW ) { // 如果编码器开关按下延迟 (250); // 去抖动开关 if (AzEncBut ==1){ AzEncBut =10; ComAzim =int(ComAzim/10)*10; // 10 度角的 ComAzim。步骤 } else { AzEncBut =1; } }} //end ReadAzimEncoder()// 状态改变时中断void doEncoderA() { // 去抖动 if ( rotation ) delay (1); // 稍等片刻,直到弹跳完成 // 测试过渡,事情真的发生了变化吗? if ( digitalRead(AzEncoderPinA) !=A_set ) { // 再次去抖动 A_set =!A_set; // 调整计数器 + 如果 A 领先 B if ( A_set &&!B_set ) ComAzim +=AzEncBut; ComAzim =((ComAzim + 360) % 360); // 0 到 359 度之间的编码器位置。旋转 =假; // 不再去抖动,直到 loop() 再次命中 }}// 在 B 改变状态时中断,与上面的 A 相同void doEncoderB() { if (rotating ) delay (1);如果(digitalRead(AzEncoderPinB)!=B_set){ B_set =!B_set; // 如果 B 领先 A,则调整计数器 - 1 if ( B_set &&!A_set ) ComAzim -=AzEncBut; ComAzim =((ComAzim + 360) % 360); // 0 到 359 度之间的编码器位置。旋转 =假; } }void AzimRotate() { if (ComAzim> TruAzim) { // 确定旋转方向// 冷切换 - 改变方向前停止电机 - 保护机械和电气部件 if (AzDir ==char(127)) { // 如果之前在反方向旋转analogWrite(AzPWMPin, 0); // 停止电机 StaAzim =TruAzim; // 这将是软启动延迟 (200) 的启动方位角; // 预切换延迟 digitalWrite(AzRotPin, LOW); // 停用旋转销 - 向右旋转延迟(200); // 切换后延迟 } else { // 相同的方向,没有停止,没有延迟 digitalWrite(AzRotPin, LOW); // 停用旋转销 - 向右旋转 } AzDir =char(126); // "->" } else { if (AzDir ==char(126)) { // 如果之前在相反方向旋转analogWrite(AzPWMPin, 0); // 停止电机 StaAzim =TruAzim; // 这将是软启动延迟 (200) 的启动方位角; // 预切换延迟 digitalWrite(AzRotPin, HIGH); // 激活旋转销 - 向左旋转延迟(200); // 切换后延迟 } else { // 相同的方向,没有停止,没有延迟 digitalWrite(AzRotPin, HIGH); // 激活旋转销 - 向左旋转 } AzDir =char(127); // "<-" } lcd.setCursor(8, 0); lcd.print(String(AzDir)); // 这会激活与角度误差成比例的 azim PWM 引脚(以百分比计算) PwAzStop =PwAzMin + round((abs(ComAzim-TruAzim))*(PwAzMax-PwAzMin)/Amax); //输出与软停止角度差成比例的功率的公式 PwAzStar =PwAzMin + round((abs(StaAzim-TruAzim))*(PwAzMax-PwAzMin)/Amax); //公式输出与软启动角度差成比例的功率 if (PwAzStar> PwAzStop){ PwAz =PwAzStop; //选择最小的值 } else {PwAz =PwAzStar;} if (PwAz> PwAzMax) {PwAz =PwAzMax;} ​​analogWrite(AzPWMPin, round(2.55*PwAz)); // 激活 Azim 驱动 PWM 引脚 }// 结束 AzimRotate()void ElevRotate() {// 以确定旋转方向 if (ComElev> TruElev) { if (ElUp ==1) { // 如果之前在相反方向旋转方向模拟写入(ElPWMPin,0); // 停止电机 StaElev =TrueElev; // 这将是软启动延迟(200)的启动高度; // 预切换延迟 digitalWrite(ElRotPin, LOW); // 停用旋转销 - 向上旋转延迟(200); // 切换后延迟 } else { // 相同的方向,没有停止,没有延迟 digitalWrite(ElRotPin, LOW); // 停用旋转销 - 向上旋转 } lcd.setCursor(8, 1);液晶显示器(2); // 向上箭头 ElUp =2; // 标高向上的标志 } else { if (ElUp ==2) { // 如果之前在相反方向旋转analogWrite(ElPWMPin, 0); // 停止电机 StaElev =TrueElev; // 这将是软启动延迟(200)的启动高度; // 预切换延迟 digitalWrite(ElRotPin, HIGH); // 停用旋转销 - 向上旋转延迟(200); // 切换后延迟 } else { // 相同的方向,没有停止,没有延迟 digitalWrite(ElRotPin, HIGH); // 停用旋转销 - 向上旋转 } lcd.setCursor(8, 1);液晶显示器(1); // 向下箭头 ElUp =1; // 标高 DN } // 这会激活与角度误差成比例的方位角 PWM 引脚(以百分比计算) PwElStop =PwElMin + round((abs(ComElev-TruElev))*(PwElMax-PwElMin)/Emax); //输出与软停止角度差成正比的功率的公式 PwElStar =PwElMin + round((abs(StaElev-TruElev))*(PwElMax-PwElMin)/Emax); //软启动输出与角度差成正比的功率的公式 if (PwElStar> PwElStop){ PwEl =PwElStop; //选择最小的值 } else {PwEl =PwElStar;} if (PwEl> PwElMax) {PwEl =PwElMax;} ​​analogWrite(ElPWMPin, round(2.55*PwEl)); // 激活 Elev 驱动 PWM 引脚}// 结束 ElevRotate()void SerComm() { // 初始化读数 ComputerRead ="";方位角 ="";海拔 =""; while(Serial.available()) { ComputerRead=Serial.readString(); // 将传入数据读取为字符串// Serial.println(ComputerRead); // 回显接收以进行测试 } // 寻找命令  for (int i =0; i <=ComputerRead.length(); i++) { if ((ComputerRead.charAt(i) ==' A')&&(ComputerRead.charAt(i+1) =='Z')){ // 如果读取 AZ for (int j =i+2; j <=ComputerRead.length(); j++) { if (isDigit (ComputerRead.charAt(j))) { // 如果字符是数字 Azimuth =Azimuth + ComputerRead.charAt(j); } else {break;} } } } // 寻找命令  for (int i =0; i <=(ComputerRead.length()-2); i++) { if ((ComputerRead.charAt(i ) =='E')&&(ComputerRead.charAt(i+1) =='L')){ // 如果读取 EL if ((ComputerRead.charAt(i+2)) =='-') { ComElev =0; // 如果高程负中断; } for (int j =i+2; j <=ComputerRead.length(); j++) { if (isDigit(ComputerRead.charAt(j))) { // 如果字符是数字 Elevation =Elevation + ComputerRead.charAt( j); } else {break;} } } } // 如果  收到 if (Azimuth !=""){ ComAzim =Azimuth.toInt(); ComAzim =ComAzim%360; // 将值保持在限制之间(对于旋转超过 360 度的跟踪器) }// 如果  收到 if (Elevation !=""){ ComElev =Elevation.toInt(); if (ComElev>180) { ComElev =0;} if (ComElev>90​​) { //如果收到超过 90deg. (对于 180 度仰角的跟踪器)ComElev =180-ComElev; //保持在90度以下。 ComAzim =(ComAzim+180)%360; // 并旋转背面的天线 } }// 寻找  对天线位置的询问 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')){ // 发回天线位置 <+xxx.x xx.x> ComputerWrite ="+"+String(TruAzim)+".0 "+String(TruElev)+". 0"; Serial.println(ComputerWrite); } }}//结束SerComm()
ant-rot-AC-aug2021Arduino
确保您使用交流电机的电气图
提供干触点(开/关)。它可以很容易地与商业旋转器连接。
/* 用于 Arduino 的 AZ/EL 天线旋转器控制器 - 交流电机 * ======================================================* 计算机使用 EasyComm 协议 - 跟踪软件 * 通过两个旋转编码器手动命令AZ - EL * * 兼容开关盒旋转器 * 或交流电机 * 左右、上下的干式触点 * * Viorel Racoviteannu / * https://www.youtube.com/channel/UCiRLZX0bV9rS04BGAyUf-fA * https://racov.ro * [email protected] * * 我不对滥用此代码* 或使用此代码可能造成的任何损害承担任何责任。 * * 2020 年 12 月 v2 - 提高了串行通信稳定性 * 2021 年 1 月 - 修复了 AZ,电机激活的 EL 容差 * 2021 年 4 月 - 提高了串行通信稳定性 * 2021 年 8 月 - 更快的 USB 更新,冷切换 Az/El 方向,代码中的小优化*/ #include  // I2C 通信库#include  // https://www.arduinolibraries.info/libraries/liquid-crystal-i2-c(LCD 库)//接线:SDA 引脚连接到 A4,SCL 引脚连接到 A5。// 通过 I2C 连接到 LCD,默认地址 0x27(A0-A2 不跳线)LiquidCrystal_I2C lcd(0x27, 16, 2); // 地址、字符、行。// 为向上/向下箭头字节声明自定义符号 DownArrow[8] ={ B00000, B00100, B00100, B00100, B10101, B01110, B00100, B00000}; byte UpArrow[8] ={ B00000, B00100, B01110, B10101, B00100, B00100, B00100, B00000};//天线电位器校准 int AzMin =1; //开始电位器 int AzMax =1023; //电位器结束 int ElMin =1; int ElMax =1023;// 天线不会移动的允许错误 int AzErr =8; int ElErr =4;// Azim 编码器变量 enum AzPinAssignments { AzEncoderPinA =2, // 编码器右侧 AzEncoderPinB =3, // 编码器左侧 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; } 延迟(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;/****************************************************************************/

示意图

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.

制造工艺

  1. 使用按钮通过 PWM 调光灯光
  2. Arduino Gyroscope Game with MPU-6050
  3. 带 LED 和压电扬声器的 DHT11 传感器
  4. Unopad - 带有 Ableton 的 Arduino MIDI 控制器
  5. 钢铁侠
  6. 带有 Arduino 的简单障碍物传感器
  7. 找到我
  8. Arduino 加湿器控制
  9. 4x4x4 LED 立方体,带有 Arduino Uno 和 1sheeld
  10. Arduino Joystick
  11. 计步器(Arduino 101)
  12. Arduino 混色器