// FlipFlat based on the Altinak Flip-Flat commands. Makes it possible to use it with ASCOM and INDI drivers // Enable DEBUG in FlipFlat.h to see logging. // Due to driver imitations in de indi driver, when using ESP32 Serial can't be used for FlipFlat communication. // Use Serial 2 (pins 16 and 17) instead. Standard Serial is used for debug messages #include int deviceId = FLIP_FLAT; // set this to FLAT_MAN if you want to remove or not use the motor handling int motorStatus = STOPPED; int lightStatus = OFF; int coverStatus = CLOSED; int motorDirection = NONE; float currentAngle = 0.0; int brightness = 0; EasyButton brightness_plus(BRIGHT_PLUS); EasyButton brightness_min(BRIGHT_MIN); EasyButton cover_control(COVER_FF); AccelStepper stepper(AccelStepper::DRIVER, STEP_PIN, DIR_PIN); void setup() { setupSerial(); LOG("Debug mode"); LOG("Starting FlipFlat"); LOG("Setup PWM pin"); pinMode(PWM, OUTPUT); LOG("Setup brightness buttons"); brightness_plus.begin(); brightness_plus.onPressed(increaseBrightness); brightness_min.begin(); brightness_min.onPressed(decreaseBrightness); LOG("Setup open-close button"); cover_control.begin(); cover_control.onPressed(handleCoverSwitch); LOG("Initializing stepper"); stepper.setMaxSpeed(1000); stepper.setAcceleration(50); stepper.setSpeed(200); stepper.moveTo(STEPS); } void loop() { brightness_plus.read(); brightness_min.read(); cover_control.read(); if (FF_SERIAL.available() > 0) // all incoming communications are fixed length at 6 bytes including the \r { LOG("Command received"); parseCommand(); } } void increaseBrightness() { LOG("Brightness+ is pressed"); brightness++; setBrightness(brightness); } void decreaseBrightness() { LOG("Brightness- is pressed"); brightness--; setBrightness(brightness); } void setBrightness(int newBrightness) { LOG1("Setting brightness to ", newBrightness); analogWrite(PWM, newBrightness); } void parseCommand() { LOG("Parse command"); char *cmd; char *data; char str[20]; memset(str, 0, 20); FF_SERIAL.readBytesUntil('\n', str, 20); cmd = str + 1; data = str + 2; // useful for debugging to make sure your commands came through and are parsed correctly. LOG1("cmd = ", cmd); LOG1("data = ", data); processCommand(cmd, data); while (FF_SERIAL.available() > 0) FF_SERIAL.read(); } void processCommand(const char *cmd, const char *data) { switch (*cmd) { /* Ping device Request: >POOO\r Return : *PiiOOO\n ii = DEVICE_ID */ case 'P': LOG("Ping received"); FF_SERIAL << F("*P") << deviceId << F("OOO\n"); break; /* Open shutter Request: >OOOO\r Return : *OiiOOO\n ii = deviceId This command is only supported on the Flip-Flat! */ case 'O': LOG("Open received"); FF_SERIAL << F("*O") << deviceId << F("OOO\n"); openFlipFlat(); break; /* Close shutter Request: >COOO\r Return : *CiiOOO\n ii = deviceId This command is only supported on the Flip-Flat! */ case 'C': LOG("Close received"); FF_SERIAL << F("*C") << deviceId << F("OOO\n"); closeFlipFlat(); break; /* Turn light on Request: >LOOO\r Return : *LiiOOO\n ii = deviceId */ case 'L': LOG("Turn on received"); FF_SERIAL << F("*L") << deviceId << F("OOO\n"); lightStatus = ON; setBrightness(brightness); break; /* Turn light off Request: >DOOO\r Return : *DiiOOO\n id = deviceId */ case 'D': LOG("Turn off received"); FF_SERIAL << F("*D") << deviceId << F("OOO\n"); lightStatus = OFF; setBrightness(0); break; /* Set brightness Request: >Bxxx\r xxx = brightness value from 000-255 Return : *Bidyyy\n id = deviceId yyy = value that brightness was set from 000-255 */ case 'B': LOG("Set brightness received"); FF_SERIAL << F("*B") << deviceId << _WIDTHZ(brightness, 3) << F("\n"); brightness = atoi(data); if (lightStatus == ON) setBrightness(brightness); break; /* Get brightness Request: >J000\r Return : *Jiiyyy\n id = deviceId yyy = current brightness value from 000-255 */ case 'J': LOG("Get brightness received"); FF_SERIAL << F("*J") << deviceId << _WIDTHZ(brightness, 3) << F("\n"); break; /* Get device status: Request: >S000\r Return : *SiiMLC\n ii = deviceId M = motor status( 0 stopped, 1 running) L = light status( 0 off, 1 on) C = Cover Status( 0 moving, 1 closed, 2 open) */ case 'S': LOG("Get status recived"); FF_SERIAL << F("*S") << deviceId << motorStatus << lightStatus << coverStatus << F("\n"); break; /* Get firmware version Request: >V000\r Return : *Vii001\n ii = deviceId */ case 'V': // get firmware version LOG("Get firmware version received"); FF_SERIAL << F("*V") << deviceId << F("001") << F("\n"); break; } } void openFlipFlat() { if (coverStatus != OPEN) { LOG("Turn light off"); setBrightness(0); motorDirection = OPENING; rotateMotor(270.0); } } void closeFlipFlat() { LOG("Closing FlipFlat"); if (coverStatus != CLOSED) { motorDirection = CLOSING; rotateMotor(270.0); } else { LOG("FlipFlap already closed"); } } void rotateMotor(float newAngle) { if (motorDirection == OPENING) { motorStatus = RUNNING; coverStatus = NEITHER_OPEN_NOR_CLOSED; stepper.move(STEPS * newAngle / 360.0); stepper.runToPosition(); coverStatus = OPEN; } else { if (motorDirection == CLOSING) { motorStatus = RUNNING; coverStatus = NEITHER_OPEN_NOR_CLOSED; stepper.move(-STEPS * newAngle / 360.0); stepper.runToPosition(); coverStatus = CLOSED; } } motorStatus = STOPPED; motorDirection = NONE; } void setupSerial() { #ifdef ESP32 FF_SERIAL.begin(9600, SERIAL_8N1, 16, 17); LOG_SERIAL.begin(9600); #endif #ifdef ARDUINO_AVR_LEONARDO FF_SERIAL.begin(9600); LOG_SERIAL.begin(9600); #endif LOG("Logging serial up and running"); LOG("FlipFlat Serial up and running"); } void handleCoverSwitch(){ switch (coverStatus){ case CLOSED: LOG("Opening cover"); openFlipFlat(); break; case OPEN: LOG("Closing cover"); closeFlipFlat(); break; default: LOG("Cover not open or closed"); } }