diff --git a/README.md b/README.md index 1770627..86a7170 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,8 @@ The controller uses the protocol Moonlite (as documented in the indilib project) This is a port of an awesome code from https://github.com/Hansastro/Focuser to ESP32 platform with some additions: 1. It uses TMC2209 stepper motor driver (because it's cool and really silent). -1. It uses an optical rotary encoder connected to pins 2 and 15 as a handcontroller, which helps manually focus your telescope on a not-really-stable mounts. The encoder focusing control feels very direct and highly resolved (because it has about 1:5 reduction). +1. It uses an optical rotary encoder connected to pins 2 and 15 as a handcontroller, which helps manually focus your telescope on a not-really-stable mounts. The encoder focusing control feels very direct and highly resolved (because it has about 1:5 reduction). +1. **Variable speed encoder control** - inspired by https://github.com/semenmiroshnichenko/Superfok, the encoder now uses a logarithmic speed multiplier that adjusts the motor steps based on rotation speed. Fast encoder rotations result in more motor steps per encoder step, providing both precision for fine adjustments and speed for coarse adjustments. # Hardware If you want to build a focuser controller by yourself, please check out a hardware repository https://github.com/simonachmueller/ESP32Focuser-hardware diff --git a/src/main.cpp b/src/main.cpp index f51925f..33b006b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -29,6 +29,10 @@ const int encoderMotorstepsRelation = 5; unsigned long timestamp; unsigned long displayTimestamp; +// Variables for variable speed encoder handling +long lastEncoderCount = 0; +long virtualMotorPosition = 0; // Accumulated motor position with speed adjustments + //LM335 TemperatureSensor(temperatureSensorPin); StepperControl Motor(stepPin, directionPin, @@ -166,11 +170,15 @@ void processCommand() case ML_SN: // Set the target position encoder.setCount(SerialProtocol.getCommand().parameter * encoderMotorstepsRelation); + lastEncoderCount = encoder.getCount(); + virtualMotorPosition = SerialProtocol.getCommand().parameter; Motor.setTargetPosition(SerialProtocol.getCommand().parameter); break; case ML_SP: // Set the current motor position encoder.setCount(SerialProtocol.getCommand().parameter * encoderMotorstepsRelation); + lastEncoderCount = encoder.getCount(); + virtualMotorPosition = SerialProtocol.getCommand().parameter; Motor.setCurrentPosition(SerialProtocol.getCommand().parameter); break; case ML_PLUS: @@ -220,14 +228,50 @@ void setup() //displayTimestamp = millis(); SetupEncoder(); + + // Initialize tracking variables + lastEncoderCount = encoder.getCount(); + virtualMotorPosition = Motor.getCurrentPosition(); } void HandleHandController() { long targetPosition = Motor.getTargetPosition(); - long encoderPosition = encoder.getCount() / encoderMotorstepsRelation; - Motor.setTargetPosition(encoderPosition); - if(targetPosition != encoderPosition) + long currentEncoderCount = encoder.getCount(); + + // Calculate encoder change since last call + long encoderDelta = currentEncoderCount - lastEncoderCount; + + if (encoderDelta != 0) + { + // Calculate speed multiplier using logarithmic function similar to Superfok + // This makes fast rotations result in more motor steps per encoder step + long absEncoderDelta = abs(encoderDelta); + + // Apply logarithmic scaling: log(n+1) gives smooth acceleration + // For absEncoderDelta=1: log(2)≈0.69, for 5: log(6)≈1.79, for 10: log(11)≈2.4 + float speedMultiplier = log((float)(absEncoderDelta + 1)); + + // Calculate motor steps with speed multiplier + // Base relation * encoder change * speed multiplier + long motorSteps = (long)(encoderMotorstepsRelation * absEncoderDelta * speedMultiplier); + + // Apply direction + if (encoderDelta > 0) + { + virtualMotorPosition += motorSteps; + } + else + { + virtualMotorPosition -= motorSteps; + } + + // Update tracking variable + lastEncoderCount = currentEncoderCount; + } + + Motor.setTargetPosition(virtualMotorPosition); + if(targetPosition != virtualMotorPosition) { Motor.goToTargetPosition(); }