#include #include ///////////////////////////////// // 10/06/2026 // Version 8.2 // Por: LU4EG ///////////////////////////////// // ============================== // LCD // ============================== LiquidCrystal lcd(2, 3, 4, 5, 6, 7); bool autoMode = false; bool lastConfigBtn = HIGH; unsigned long configPressTime = 0; // ============================== // RELAYS // ============================== #define RELAY_AZ_CW 12 #define RELAY_AZ_CCW 13 #define RELAY_EL_UP A2 #define RELAY_EL_DOWN A3 // ============================== // BUTTONS // ============================== #define BTN_AZ_CW 9 #define BTN_AZ_CCW 8 #define BTN_EL_DOWN 11 #define BTN_EL_UP A4 #define BTN_CONFIG 10 // ============================== // POTS // ============================== #define POT_AZ A0 #define POT_EL A1 // ============================== // CALIB DATA // ============================== struct CalibData { int azMin; int azMax; int elMin; int elMax; }; CalibData calib; // ============================== // TARGET GS-232 // ============================== int targetAz = 0; int targetEl = 0; char serialBuf[20]; byte serialPos = 0; // ============================== // CONTROL // ============================== #define AZ_DEADBAND 2 #define EL_DEADBAND 1 enum Dir { STOP, CW, CCW }; Dir azDir = STOP; Dir elDir = STOP; // ============================== // LCD CONTROL // ============================== unsigned long lastLCD = 0; const int lcdInterval = 200; // ============================== // CONFIG // ============================== byte cfgStep = 0; bool inConfig = false; // ============================== // LCD helper // ============================== void printLine(byte row, const char *msg) { lcd.setCursor(0, row); for (int i = 0; i < 16; i++) { lcd.print(msg[i] ? msg[i] : ' '); } } // ============================== // STOP // ============================== void stopAll() { digitalWrite(RELAY_AZ_CW, LOW); digitalWrite(RELAY_AZ_CCW, LOW); digitalWrite(RELAY_EL_UP, LOW); digitalWrite(RELAY_EL_DOWN, LOW); } // ============================== // ADC smoothing // ============================== int smoothRead(int pin) { long sum = 0; for (int i = 0; i < 5; i++) sum += analogRead(pin); return sum / 5; } // ============================== // AZ // ============================== int readAz() { return constrain( map(smoothRead(POT_AZ), calib.azMin, calib.azMax, 0, 359), 0, 359); } // ============================== // EL // ============================== int readEl() { return constrain( map(smoothRead(POT_EL), calib.elMin, calib.elMax, 0, 90), 0, 90); } // ============================== // SERIAL GS-232 // ============================== void handleSerial() { while (Serial.available()) { char c = Serial.read(); if (c == '\n' || c == '\r') { serialBuf[serialPos] = 0; if (serialBuf[0] == 'W') { int az, el; if (sscanf(serialBuf, "W%d %d", &az, &el) == 2) { targetAz = constrain(az, 0, 359); targetEl = constrain(el, 0, 90); } } serialPos = 0; } else { if (serialPos < sizeof(serialBuf) - 1) serialBuf[serialPos++] = c; } } } // ============================== // AUTO AZ // ============================== Dir autoAzDir() { int actual = readAz(); int cw = (actual - targetAz + 360) % 360; int ccw = (targetAz - actual + 360) % 360; if (cw <= AZ_DEADBAND || ccw <= AZ_DEADBAND) return STOP; return (ccw < cw) ? CCW : CW; } // ============================== // AUTO EL // ============================== Dir autoElDir() { int err = targetEl - readEl(); if (abs(err) <= EL_DEADBAND) return STOP; return (err > 0) ? CW : CCW; } // ============================== // INPUTS // ============================== void readInputs() { bool azCW = digitalRead(BTN_AZ_CW) == LOW; bool azCCW = digitalRead(BTN_AZ_CCW) == LOW; bool elUP = digitalRead(BTN_EL_UP) == LOW; bool elDN = digitalRead(BTN_EL_DOWN) == LOW; int az = readAz(); int el = readEl(); // ========================== // AUTO MODE (IGNORA BOTONES) // ========================== if (autoMode) { azDir = autoAzDir(); elDir = autoElDir(); return; } // ========================== // MANUAL MODE // ========================== // -------------------------- // AZIMUT (0–359) // -------------------------- if (azCW && !azCCW) { if (az >= 359) { azDir = STOP; // límite superior } else { azDir = CW; } } else if (azCCW && !azCW) { if (az <= 0) { azDir = STOP; // límite inferior } else { azDir = CCW; } } else { azDir = STOP; } // -------------------------- // ELEVACIÓN (0–90) // -------------------------- if (elUP && !elDN) { if (el >= 90) { elDir = STOP; } else { elDir = CW; } } else if (elDN && !elUP) { if (el <= 0) { elDir = STOP; } else { elDir = CCW; } } else { elDir = STOP; } } // ============================== // DISPLAY // ============================== void handleNormal() { int az = readAz(); int el = readEl(); digitalWrite(RELAY_AZ_CW, azDir == CW); digitalWrite(RELAY_AZ_CCW, azDir == CCW); digitalWrite(RELAY_EL_UP, elDir == CW); digitalWrite(RELAY_EL_DOWN, elDir == CCW); char l1[17] = " "; char l2[17] = " "; // snprintf(l1, 17, "TA:%03d TE:%02d %s", // targetAz, // targetEl, // autoMode ? "Aut" : "Man"); if (autoMode) { snprintf(l1, 17, "TA:%03d TE:%02d Aut", targetAz, targetEl); } else { snprintf(l1, 17, "TA:--- TE:-- Man"); } snprintf(l2, 17, "%cA:%03d %cE:%02d", (azDir == CW) ? '>' : (azDir == CCW ? '<' : '*'), az, (elDir == CW) ? '^' : (elDir == CCW ? 'v' : '*'), el); printLine(0, l1); printLine(1, l2); } void handleCalibration() { static bool released = false; stopAll(); if (!released) { if (digitalRead(BTN_CONFIG) == HIGH) released = true; return; } switch (cfgStep) { case 0: printLine(0, "CAL1 AZ=180"); lcd.setCursor(0,1); lcd.print("ADC:"); lcd.print(smoothRead(POT_AZ)); lcd.print(" "); if (digitalRead(BTN_CONFIG) == LOW) { calib.azMin = smoothRead(POT_AZ); while (digitalRead(BTN_CONFIG) == LOW); delay(200); cfgStep++; } break; case 1: printLine(0, "CAL2 AZ=181"); lcd.setCursor(0,1); lcd.print("ADC:"); lcd.print(smoothRead(POT_AZ)); lcd.print(" "); if (digitalRead(BTN_CONFIG) == LOW) { calib.azMax = smoothRead(POT_AZ); while (digitalRead(BTN_CONFIG) == LOW); delay(200); cfgStep++; } break; case 2: printLine(0, "CAL3 EL=000"); lcd.setCursor(0,1); lcd.print("ADC:"); lcd.print(smoothRead(POT_EL)); lcd.print(" "); if (digitalRead(BTN_CONFIG) == LOW) { calib.elMin = smoothRead(POT_EL); while (digitalRead(BTN_CONFIG) == LOW); delay(200); cfgStep++; } break; case 3: printLine(0, "CAL4 EL=090"); lcd.setCursor(0,1); lcd.print("ADC:"); lcd.print(smoothRead(POT_EL)); lcd.print(" "); if (digitalRead(BTN_CONFIG) == LOW) { calib.elMax = smoothRead(POT_EL); EEPROM.put(0, calib); lcd.clear(); printLine(0, "CAL OK"); printLine(1, "EEPROM SAVED"); delay(2000); released = false; inConfig = false; cfgStep = 0; } break; } } // ============================== // SETUP // ============================== void setup() { Serial.begin(9600); pinMode(RELAY_AZ_CW, OUTPUT); pinMode(RELAY_AZ_CCW, OUTPUT); pinMode(RELAY_EL_UP, OUTPUT); pinMode(RELAY_EL_DOWN, OUTPUT); pinMode(BTN_AZ_CW, INPUT_PULLUP); pinMode(BTN_AZ_CCW, INPUT_PULLUP); pinMode(BTN_EL_DOWN, INPUT_PULLUP); pinMode(BTN_EL_UP, INPUT_PULLUP); pinMode(BTN_CONFIG, INPUT_PULLUP); lcd.begin(16, 2); if (digitalRead(BTN_CONFIG) == LOW) { inConfig = true; } lcd.clear(); lcd.setCursor(0, 0); lcd.print(" LU4EG GS-232 "); lcd.setCursor(0, 1); lcd.print(" Iniciando. "); delay(500); lcd.setCursor(0, 1); lcd.print(" Iniciando.. "); delay(500); lcd.setCursor(0, 1); lcd.print(" Iniciando... "); delay(500); stopAll(); EEPROM.get(0, calib); targetAz = readAz(); targetEl = readEl(); } // ============================== // LOOP // ============================== void loop() { if (inConfig) { handleCalibration(); return; } bool cfg = digitalRead(BTN_CONFIG); if (lastConfigBtn == HIGH && cfg == LOW) configPressTime = millis(); if (lastConfigBtn == LOW && cfg == HIGH) { if (millis() - configPressTime < 600) { autoMode = !autoMode; stopAll(); } } lastConfigBtn = cfg; handleSerial(); readInputs(); if (millis() - lastLCD > lcdInterval) { lastLCD = millis(); handleNormal(); } }