Controlling Telescope Motors - Position and Speed Issues

Hello everyone, this is my first post here, and it concerns my first Arduino project which I've been working on for a couple of weeks. I have tried to follow the forum guidelines on code etc, but please bear with me if I don't get the formatting right first time. Also, apologies if this post is a bit long-winded; I just want to give as much relevant information as possible. A bit of background:

I am fairly comfortable with the mechanical side of things, and figuring out wiring and prototyping the hardware. I have to admit I am struggling with the coding, and have probably relied too much on AI rather than sticking at learning the coding better.

I have recently built a DIY Dobsonian telescope mount (basic altitude and azimuth). Altitude is swinging the 'scope up and down vertically, azimuth is rotating the whole thing on its base such that it can be manually pointed at any target in the sky. I also built a DIY equatorial platform, which couteracts the Earth's rotation so that images in the eyepiece don't slowly drift out of sight. This can be particularly annoying at high magnifications, when a target can move across the field of view in minutes (or less) if it's not compensated for. The equatorial platform is currently powered by a small off-the-shelf 9V DC motor/gearbox, and works very well in terms of freezing a target object. This is the setup pretty much as it is now:

This is all great, but trying to manually nudge the 'scope to center a star or planet at high magnification in the eyepiece is literally hit-and-miss, especially with the inherent stick/slip of the bearings - and it gets frustrating. I thought if I could power the altitude & azimuth axes with stepper motors, and be able to step them in tiny increments with encoders, this would be great; I'd be able to leave the tracking motor running and fine-tune the position of the image in the eyepiece, then, when set it would stay there. I did some calculations and all seemed well in terms of gearing and steps/microsteps etc.

I obtained some NEMA17 stepper motors (17HS13-0404S), and an Elegoo UNO R3 starter pack, and got started. A colleague at work suggested Adafruit's TMC2209 Stepper Motor Driver Breakout Boards, and Adafruit I2C QT Rotary Encoder Breakouts would be suitable. I'm using a 12V 2A plug-in power supply, in conjunction with a 330 microfarad, 16V capacitor. Another colleague lent me his "Arduino for Dummies" book. I got to work wiring everything up, and got AI to write me some code. Eventually I got the two steppers working nicely with the encoders. I have built a leadscrew mechanism to drive the altitude axis, and am working on the azimuth mechanism.

I then thought why not also replace the 9V battery powered equatorial drive motor with another stepper - I could after all then just use one battery for all motors, and the stepper would hopefully be more consistent. I then prototyped a simple variable speed drive using a 10 K Ohm potentiometer. Again I got AI to write the code, and after a bit of fiddling around got it working well, the speed varying as it should. However...when I come to combining the two sets of code - position and speed - into one (again using AI), the speed control doesn't work properly. Rather than varying from zero (or a minimum) to the maximum, it only runs at one speed. Or at least any variation in speed is imperceptible. When run in isolation with its own code, the speed range on that motor is obvious when adjusted with the potentiometer. The actual speed I'll need for this drive is very low - about 0.09 r.p.m. (in case that's relevant to the discussion).

I have tried removing the encoder bits of code completely, and then the motor speed control works as it should. It has been suggested by my collegue that the issue may be with how the program is being read and the processing speed of the Arduino. I have fiddled with "Delay" times, but to no avail.

Here is the combined code (I can post the individual codes that both work if necessary):

#include <Wire.h>
#include <Adafruit_seesaw.h>
#include <AccelStepper.h>

// Create seesaw objects for two encoders
Adafruit_seesaw encoder1;
Adafruit_seesaw encoder2;

// Define TMC2209 pins for Motor 1 (Encoder-controlled)
#define DIR_PIN1 2
#define STEP_PIN1 3
#define ENABLE_PIN1 5

// Define TMC2209 pins for Motor 2 (Encoder-controlled)
#define DIR_PIN2 6
#define STEP_PIN2 7
#define ENABLE_PIN2 8

// Define pins for Motor 3 (Potentiometer-controlled with AccelStepper)
#define DIR_PIN3 10
#define STEP_PIN3 11
#define POT_PIN A0

// Define scaling factor for encoder to motor steps
const int stepsPerEncoderStep1 = 4;
const int stepsPerEncoderStep2 = 4;

int16_t lastEncoderPosition1 = 0;
int16_t lastEncoderPosition2 = 0;

// Create AccelStepper object for Motor 3
AccelStepper stepper(AccelStepper::DRIVER, STEP_PIN3, DIR_PIN3);

void setup() {
  // Initialize serial for debugging
  Serial.begin(115200);

  // Initialize I2C for Encoder 1
  if (!encoder1.begin(0x36)) {
    Serial.println("Encoder 1 not found!");
    while (1);
  }

  // Initialize I2C for Encoder 2
  if (!encoder2.begin(0x37)) { // Assuming encoder 2 is on address 0x37
    Serial.println("Encoder 2 not found!");
    while (1);
  }

  // Initialize TMC2209 pins for Motor 1
  pinMode(DIR_PIN1, OUTPUT);
  pinMode(STEP_PIN1, OUTPUT);
  pinMode(ENABLE_PIN1, OUTPUT);
  digitalWrite(ENABLE_PIN1, LOW);

  // Initialize TMC2209 pins for Motor 2
  pinMode(DIR_PIN2, OUTPUT);
  pinMode(STEP_PIN2, OUTPUT);
  pinMode(ENABLE_PIN2, OUTPUT);
  digitalWrite(ENABLE_PIN2, LOW);

  // Initialize TMC2209 pins for Motor 3
  pinMode(DIR_PIN3, OUTPUT);

  // Set up AccelStepper for Motor 3
  stepper.setMaxSpeed(8000); // Steps per second
  stepper.setAcceleration(500); // Steps per second^2
  digitalWrite(DIR_PIN3, HIGH); // Set direction to forward

  // Start at position 0 for both encoders
  lastEncoderPosition1 = encoder1.getEncoderPosition();
  lastEncoderPosition2 = encoder2.getEncoderPosition();
}

void loop() {
  // Handle Encoder 1 and Motor 1
  int16_t encoderPosition1 = encoder1.getEncoderPosition();
  int stepsToMove1 = (encoderPosition1 - lastEncoderPosition1) * stepsPerEncoderStep1;
  if (stepsToMove1 != 0) {
    digitalWrite(DIR_PIN1, stepsToMove1 > 0 ? HIGH : LOW);
    for (int i = 0; i < abs(stepsToMove1); i++) {
      digitalWrite(STEP_PIN1, HIGH);
      delayMicroseconds(100000);
      digitalWrite(STEP_PIN1, LOW);
      delayMicroseconds(100000);
    }
    lastEncoderPosition1 = encoderPosition1;
  }

  // Handle Encoder 2 and Motor 2
  int16_t encoderPosition2 = encoder2.getEncoderPosition();
  int stepsToMove2 = (encoderPosition2 - lastEncoderPosition2) * stepsPerEncoderStep2;
  if (stepsToMove2 != 0) {
    digitalWrite(DIR_PIN2, stepsToMove2 > 0 ? HIGH : LOW);
    for (int i = 0; i < abs(stepsToMove2); i++) {
      digitalWrite(STEP_PIN2, HIGH);
      delayMicroseconds(100000);
      digitalWrite(STEP_PIN2, LOW);
      delayMicroseconds(100000);
    }
    lastEncoderPosition2 = encoderPosition2;
  }

  // Debugging information for encoders
  Serial.print("Encoder 1 Position: ");
  Serial.println(encoderPosition1);
  Serial.print("Steps for Motor 1: ");
  Serial.println(stepsToMove1);
  Serial.print("Encoder 2 Position: ");
  Serial.println(encoderPosition2);
  Serial.print("Steps for Motor 2: ");
  Serial.println(stepsToMove2);

  // Handle Potentiometer and Motor 3
  int potValue = analogRead(POT_PIN);
  float motorSpeed = map(potValue, 0, 1023, 0, 8000);
  stepper.setSpeed(motorSpeed);
  stepper.runSpeed();
}

Here is the circuit diagram (apologies if it's unclear, any suggestions for improving it gratefully accepted):

Any suggestions on how to get this code working as one would be really appreciated.

Another question is regarding back-driving the azimuth motor: As I mentioned, this will be a friction drive of a small pulley against a larger slew bearing (actually a re-purposed lazy susan base). I am designing the motor/pulley to be easily brought into and out of contact using an over-centre mechanism. However, if I forget to dis-engage it, or accidentally nudge the 'scope, I guess this could overcome the holding torque of the motor and back-drive it at quite a speed. Is this an issue? Should I incorporate some kind of diode system? It's not a problem with the altitude mechanism, becasue the leadscrew can't be back-driven.

Thanks very much in advance for any suggestions.

This is the manual version of the altitude fine adjustment, with torque arm and clutch handwheel for rapid movements:

And this is the modified version with the motor mount. I retained one hand wheel for manual fine adjustment if I ever need it:

This is the equatorial drive, showing the 9V motor on the right, which friction-drives the black plastic sectors to rotate the platform. I've replaced the plastic sectors with the aluminium ones now.

This is my attempt at breadboard prototyping:

Welcome and Cool project by the way.

I'm envious of your mechanical skills - far in advance of anything I could do!

I would rate this sort of project as quite advanced - there's quite a bit going on and it has to drive the motors very precisely in order to keep the object of interest central in the FOV of the telescope - especially if you are into astro-photography with long exposure times etc.

There are a few Arduino based telescope control projects according to my quick google search. I don't know if any of them would be a suitable starting point. Some seem to use a PC program called Stellarium to send movement commands to a motorised unit.

Just my personal opinion, but I think you may struggle to get help with your project as you say that the software has been generated with the help of AI. However, there may be some stargazers on the forum that would know what to look for in the code and could possibly help you out.

I'm surprised that the 9V battery was capable of powering anything as they are generally useless for anything other than smoke alarms on old tv remotes!

I think that is your problem, it's what we call blocking code. Since you are already using AccelStepper, you might be able to use it for motor 1 and 2 as well. Otherwise, you will need to change the stepping to use millis() instead of delay.

1 Like

Yeah, we see this kind of thing a lot. But we can't help much finding errors in a plate full of spaghetti!

If you want some tips, let me know.

Thanks very much for the comments.

Just to clarify something - I'm not wanting to track stars using the two motors (alt + az), that's the job of the equatorial platform, and to be fair it does that just fine as it is.

The object of the exercise - at present anyway - is only to centre an object in the field of view, which will then stay put in the field of view as the equatorial platform tracks it.

There is no tracking accuracy required at all. What is critical is having enough step resolution to 'jog' the axes to adjust the target in the eyepiece, without that object crossing the field of view in lass than one step. Ideally I'd want a minimum of about 10 step increments to traverse an object accross the fiewld fo view at a decent magnification. As mentioned I did calcualte this, and it did seem do-able.

The 9V motor is a standard thing that seems to be used for platforms such as mine - don't forget the motor is geared down massively to 0.09 r.p.m., with a proportional increase in torque.

BTW for long exposure astrophotography, you can't really use just altitude and azimuth movements to track an object, because that doesn't take into account the rotation of the field of view. You can however do that using an "equatorial" type mount (a bit like mine), because that does rotate the telescope and so any objects in the field of view don't rotate relative to it.

Thanks - yes it was just for illustration! It basically replicates the circuit diagram I posted.

Sorry - that's my first posting error!

I changed those delays to a huge number just to see what happened, but it made no differece if they were set to 0, 100 or 1000 or whatever. By default they were at 1000.

  • Still finding out how to quote replies to give context to my responses...sorry.

I assume this is a solved problem in the amateur astronomy community and I assume there is a forum where you can find help.

When you get to the nitty-gritty details of controlling the motors, someone here may be able to help. Astronomers should have the algorithms but you may have to write the specific code to run on an Arduino and adapt it to your mechanism.

I'm not sure if AI can read a schematic (yet) but I'm pretty sure it can't understand your mechanism.

If you quote the whole of a post, then the forum software kindly removes the quote completely.

I had to alter your quote, by removing the 'sorry', so that the quote appears.

1 Like

I think the astronomy part of my post may have casued confusion, but reading the forum guidelines, it suggests giving background context and diagrams, code etc.

Fundamentally it's about not being able to simultaneously control the speed of a stepper with a pot, when simultaneously stepping two others with encoders.

That's really all the issue is.

That's another red flag, too many prints.

1 Like

OK thanks, so can I just delete them? I did use them briefly to make sure my encoders were working.

You could put them in a block comment, an #ifdef or a regular if. Even better, put them in a separate function which you can call as needed.

2 Likes

I might have misread, but a stepper using the appropriate library can get 51,200 steps per revolution. I am fairly sure that is plenty fine, in fact probably too much. I would actually consider adding a simple switch to change from coarse to fine. I do not recommend AI. I recommend finding sample code and then modify that.
I am 83 and my health is day to day, so I can't focus on this at the moment, but there are at least tens of thousands of examples of simple slew circuits.
BTW I am an astronomer and astro photographer.

1 Like

Then the problem is you did not design the program to be able to do that, but have kept on adding and adding to the program.

Actually, now that you have a good idea of what you really want to do, it's time to redo the program from the start. There is no shame in doing that because we all have done that in the past, even in commercial programming jobs.

1 Like

That's great, but how do I do it? As I said this is literally my first project.

Thanks, yes I was going to use the push-button function on the encoder to (eventually) switch from low to high rate on the knobs.