{"id":1327,"date":"2017-06-12T20:27:53","date_gmt":"2017-06-13T03:27:53","guid":{"rendered":"https:\/\/partofthething.com\/thoughts\/?p=1327"},"modified":"2024-04-11T07:59:07","modified_gmt":"2024-04-11T14:59:07","slug":"making-a-cheap-and-simple-barn-door-star-tracker-with-software-tangent-correction-for-astrophotography","status":"publish","type":"post","link":"https:\/\/partofthething.com\/thoughts\/making-a-cheap-and-simple-barn-door-star-tracker-with-software-tangent-correction-for-astrophotography\/","title":{"rendered":"Making a cheap and simple barn-door star tracker with software tangent correction for astrophotography"},"content":{"rendered":"\n<p>I like to mix hobbies, so naturally I&#8217;ve been eying astrophotography for a while. I&#8217;ve taken a time-lapse here and a moon picture there but, inspired by the folks over at <a href=\"https:\/\/www.reddit.com\/r\/astrophotography\/\">\/r\/astrophotography<\/a>,&nbsp; I wanted to take it to the next level. Since the Earth is spinning, any long exposure of the night sky has star trails, so you have to make your camera counter-spin if you want clear shots. In this post, you can read about how I made a simple barn door sky tracker to do this.<\/p>\n\n\n\n<p><a href=\"https:\/\/en.wikipedia.org\/wiki\/Barn_door_tracker\">Barn door sky trackers<\/a> have been made at home by lots of people for a long time. There are a variety of designs with different levels of complexity and precision required. I thought I&#8217;d make the simplest-to-construct one, a Haig mount. To correct he tangent error, I decided to use a cheap microcontroller (MCU) and have it speed up appropriately via software. Fun!<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter\"><a href=\"https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/before-after_sm.jpg\"><img loading=\"lazy\" decoding=\"async\" width=\"1037\" height=\"512\" src=\"https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/before-after_sm.jpg\" alt=\"\" class=\"wp-image-1356\" srcset=\"https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/before-after_sm.jpg 1037w, https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/before-after_sm-300x148.jpg 300w, https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/before-after_sm-768x379.jpg 768w, https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/before-after_sm-1024x506.jpg 1024w\" sizes=\"auto, (max-width: 1037px) 100vw, 1037px\" \/><\/a><figcaption class=\"wp-element-caption\">With\/without tracker<\/figcaption><\/figure>\n<\/div>\n\n\n<h2 class=\"wp-block-heading\">The Math<\/h2>\n\n\n\n<p>The math behind this is fun mostly because it&#8217;s straight out of high school and you finally at long last get to use it. Here&#8217;s the basic design:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><a href=\"https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/tracker_schematic.png\"><img loading=\"lazy\" decoding=\"async\" width=\"740\" height=\"582\" src=\"https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/tracker_schematic.png\" alt=\"Cartoon of barn door tracker\" class=\"wp-image-1328\" srcset=\"https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/tracker_schematic.png 740w, https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/tracker_schematic-300x236.png 300w\" sizes=\"auto, (max-width: 740px) 100vw, 740px\" \/><\/a><\/figure>\n\n\n\n<!--more-->\n\n\n\n<p>The angle must increase at a constant rate based on the rotation of the Earth. We all know the rate the world turns so we can get explicit expressions for the angle as a function of time.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter\"><a href=\"https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/theta.png\"><img loading=\"lazy\" decoding=\"async\" width=\"685\" height=\"57\" src=\"https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/theta.png\" alt=\"\" class=\"wp-image-1329\" srcset=\"https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/theta.png 685w, https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/theta-300x25.png 300w\" sizes=\"auto, (max-width: 685px) 100vw, 685px\" \/><\/a><\/figure>\n<\/div>\n\n\n<p>Since the Earth is also rotating around the Sun, it turns out that it actually takes more like&nbsp;23 hours, 56 minutes, 4.0916 seconds for the stars in the sky to rotate all the way around. This is called the <a href=\"https:\/\/en.wikipedia.org\/wiki\/Sidereal_time\">Mean Sidereal Day<\/a> and is the value we&#8217;ll actually use in this system. As you can now calculate, the constant angular velocity we need this thing to open at is 7.292115e-05 radians per second.<\/p>\n\n\n\n<p>Now we are faced with a classic word problem: &#8220;What rate should we spin the motor to maintain a constant angular velocity in the above triangle?&#8221; First let&#8217;s just see how fast the screw has to rise. Bust out some trig to get an expression for y(t) and then take its derivative (don&#8217;t forget the chain rule!) and you&#8217;ll find:<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter\"><a href=\"https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/yt.png\"><img loading=\"lazy\" decoding=\"async\" width=\"372\" height=\"129\" src=\"https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/yt.png\" alt=\"\" class=\"wp-image-1330\" srcset=\"https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/yt.png 372w, https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/yt-300x104.png 300w\" sizes=\"auto, (max-width: 372px) 100vw, 372px\" \/><\/a><\/figure>\n<\/div>\n\n\n<p>Ok, now we just have to convert that to a rotation of the screw. The threaded rod I got at Hero Ace downtown is a 1\/4-20 bolt, where the 20 means &#8220;there are 20 threads in 1 inch,&#8221; or in other words, &#8220;there are 20 rotations per 2.54 cm of insertion = 7.874 rotations\/cm.&#8221;&nbsp; Adjust accordingly if you have a different screw thread.<\/p>\n\n\n\n<p>Let&#8217;s plot the rotations per minute required to match the Earth&#8217;s rotation for varying values of L my multiplying dy\/dt [cm\/minute] by 7.874 [rotations\/cm] for 90 minutes.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter\"><a href=\"https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/rotation_rate.png\"><img loading=\"lazy\" decoding=\"async\" width=\"720\" height=\"576\" src=\"https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/rotation_rate.png\" alt=\"Rotation rate graph\" class=\"wp-image-1331\" srcset=\"https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/rotation_rate.png 720w, https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/rotation_rate-300x240.png 300w\" sizes=\"auto, (max-width: 720px) 100vw, 720px\" \/><\/a><\/figure>\n<\/div>\n\n\n<p>Note: You can check out the <a href=\"https:\/\/partofthething.com\/barn-door-jupyter-sheet.html\">Python code used to do these calculations here<\/a>.<\/p>\n\n\n\n<p>As you can see, the rate changes with time! This is called &#8220;the tangent error&#8221; and it&#8217;s caused by the fact that the acorn nut is sliding along the hypotenuse as the &#8220;barn door&#8221; opens (see schematic above). There are a bunch of ways people have dealt with it. One is to use a curved screw. Another is to put a specially-shaped cam where the acorn nut contacts the top board. But for this project, I&#8217;m simply going to speed up the motor as time goes on if I do a very long exposure. Not having a proper shop, I prefer to use simple physical construction and deal with it in code. If you&#8217;re doing 5-10 minute exposures, this issue won&#8217;t get to you. Note also that if you have a constant 1 RPM motor, a 29cm L is about right for you.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Putting it together<\/h2>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter\"><a href=\"https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/ace_receipt2.jpg\"><img loading=\"lazy\" decoding=\"async\" width=\"157\" height=\"300\" src=\"https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/ace_receipt2-157x300.jpg\" alt=\"ACE Hardware Receipt\" class=\"wp-image-1335\" srcset=\"https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/ace_receipt2-157x300.jpg 157w, https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/ace_receipt2-768x1467.jpg 768w, https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/ace_receipt2-536x1024.jpg 536w, https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/ace_receipt2.jpg 904w\" sizes=\"auto, (max-width: 157px) 100vw, 157px\" \/><\/a><\/figure>\n<\/div>\n\n\n<p>I bought some stuff at the local ACE hardware store to put this together. They had pretty much everything I needed in the hardware department. <\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter\"><a href=\"https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/IMG_6531.jpg\"><img loading=\"lazy\" decoding=\"async\" width=\"1200\" height=\"801\" src=\"https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/IMG_6531.jpg\" alt=\"Picture of hardware \" class=\"wp-image-1338\" srcset=\"https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/IMG_6531.jpg 1200w, https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/IMG_6531-300x200.jpg 300w, https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/IMG_6531-768x513.jpg 768w, https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/IMG_6531-1024x684.jpg 1024w\" sizes=\"auto, (max-width: 1200px) 100vw, 1200px\" \/><\/a><\/figure>\n<\/div>\n\n\n<p>My steps:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Cut boards to size. I chose my total board length as a few inches longer than the 29cm I wanted between my hinge and the shaft. Note that you want to give the top board enough space to open and still contact the shaft, so give it at least 2-3 inches, and longer if you want even longer exposures.<\/li>\n\n\n\n<li>Attach hinge<\/li>\n\n\n\n<li>Attach slide plate if you have one (not sure if needed)<\/li>\n\n\n\n<li>Measure out L from hinge pin and drill hole in bottom board<\/li>\n\n\n\n<li>Apply epoxy to tee-nut for drive shaft and hammer it in place<\/li>\n\n\n\n<li>Epoxy small washers to the top of rails<\/li>\n\n\n\n<li>Stick motor shaft in drive hole and mark location of rails<\/li>\n\n\n\n<li>Drill 1\/8&#8243; holes for 1\/8&#8243; rails on bottom board and slide them in<\/li>\n\n\n\n<li>Drill hole for ball mount on top board<\/li>\n\n\n\n<li>Mount camera and have a friend help balance it to try to figure out where the center of gravity is on the bottom board. Choose a spot and drill and install 2nd tee-nut for bottom tripod mount. I ended up moving mine from the bottom to the top (so the tripod screw pulls it into the wood, not out) and getting a really long tee-nut for better stability. Then I can really crank down on the tripod screw so the thing stays steady.<\/li>\n\n\n\n<li>Drill out 1\/4&#8243; hole in one side of 5mm coupling nut to attach drive shaft to motor<\/li>\n\n\n\n<li>Screw threaded rod into main hole, install acorn nut on top<\/li>\n\n\n\n<li>Put motor on tails and fire it up!<\/li>\n<\/ul>\n\n\n\n<p>Loud video warning:<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter\"><a href=\"https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/IMG_20170611_141418.jpg\"><img loading=\"lazy\" decoding=\"async\" width=\"169\" height=\"300\" src=\"https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/IMG_20170611_141418-169x300.jpg\" alt=\"In the barn\" class=\"wp-image-1347\" srcset=\"https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/IMG_20170611_141418-169x300.jpg 169w, https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/IMG_20170611_141418-768x1365.jpg 768w, https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/IMG_20170611_141418-576x1024.jpg 576w, https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/IMG_20170611_141418.jpg 1152w\" sizes=\"auto, (max-width: 169px) 100vw, 169px\" \/><\/a><\/figure>\n<\/div>\n\n\n<p><iframe loading=\"lazy\" width=\"560\" height=\"315\" src=\"https:\/\/www.youtube.com\/embed\/jtFk6VWCEik\" allowfullscreen=\"allowfullscreen\" frameborder=\"0\"><\/iframe><br><\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter\"><a href=\"https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/IMG_20170611_135908.jpg\"><img loading=\"lazy\" decoding=\"async\" width=\"576\" height=\"1024\" src=\"https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/IMG_20170611_135908-576x1024.jpg\" alt=\"The sky tracker all put together\" class=\"wp-image-1346\" srcset=\"https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/IMG_20170611_135908-576x1024.jpg 576w, https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/IMG_20170611_135908-169x300.jpg 169w, https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/IMG_20170611_135908-768x1365.jpg 768w, https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/IMG_20170611_135908.jpg 1152w\" sizes=\"auto, (max-width: 576px) 100vw, 576px\" \/><\/a><\/figure>\n<\/div>\n\n\n<h2 class=\"wp-block-heading\">Programming the Stepper Motor\/MCU<\/h2>\n\n\n\n<p>Before I started this project, I happened to have a bag full of stepper motors (<a href=\"https:\/\/www.amazon.com\/Elegoo-28BYJ-48-ULN2003-Stepper-Arduino\/dp\/B01CP18J4A\/ref=sr_1_3?s=electronics&amp;ie=UTF8&amp;qid=1497136252&amp;sr=1-3&amp;keywords=stepper+motor\">like these<\/a>) and their controllers, and also a bag full of ESP8266 microcontrollers (<a href=\"https:\/\/www.amazon.com\/Makerfocus-NodeMcu-Development-ESP8266-ESP-12F\/dp\/B01N3P763C\/ref=sr_1_2?s=electronics&amp;ie=UTF8&amp;qid=1497136326&amp;sr=1-2-spons&amp;keywords=esp8266&amp;psc=1\">like these<\/a>). These are both extremely cheap (like $5 each) items and so I figured they&#8217;d be fun to apply for this. ESP8266&#8217;s even have Wifi if you want to get super fancy but I didn&#8217;t use that here.<\/p>\n\n\n\n<div style=\"background-color: #ffffe0; margin-left: 18px; padding: 12px;\"><strong>NOTE<\/strong>: If you&#8217;d rather not bother with a MCU and variable-speed motor, you can do very well just skipping this step and using a much simpler 1 RPM motor and a L of 29 cm.<\/div>\n\n\n\n<p>Using <a href=\"https:\/\/github.com\/esp8266\/Arduino\">instructions from here<\/a>, I got my ESP8266 all hooked up and ready to program using the Arduino IDE. Here&#8217;s <a href=\"http:\/\/www.raspberrypi-spy.co.uk\/2012\/07\/stepper-motor-control-in-python\/\">a good intro<\/a> to spinning these kinds of motors with code.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Calibration of the ESP8266 delay<\/h4>\n\n\n\n<p>First thing I wanted to do was just double check that the timing would be precise enough for what I need (mostly I just wanted to fire up the scope). I got the motor turning with a 5 ms delay, double-stepping and took this measurement from one of the 4 pins:<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter\"><a href=\"https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/calibration-1.png\"><img loading=\"lazy\" decoding=\"async\" width=\"480\" height=\"234\" src=\"https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/calibration-1.png\" alt=\"Oscilloscope screen grab\" class=\"wp-image-1333\" srcset=\"https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/calibration-1.png 480w, https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/calibration-1-300x146.png 300w\" sizes=\"auto, (max-width: 480px) 100vw, 480px\" \/><\/a><\/figure>\n<\/div>\n\n\n<p>Pretty close to that 10 ms we&#8217;d expect. Looks good to me. Here&#8217;s the code:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">#define NUM_PINS 4\n#define NUM_STEPS 8\n\nint allPins[NUM_PINS] = {D1, D2, D3, D4};\n\n\/\/ from manufacturers datasheet\nint STEPPER_SEQUENCE[NUM_STEPS][NUM_PINS] = {{1,0,0,1},\n                                             {1,0,0,0},\n                                             {1,1,0,0},\n                                             {0,1,0,0},\n                                             {0,1,1,0},\n                                             {0,0,1,0},\n                                             {0,0,1,1},\n                                             {0,0,0,1}};\nint stepNum = 0;\n\nvoid setup() {\n  Serial.begin(115200);\n  setup_gpio();\n}\n\nvoid setup_gpio() {\n  for (int i=0;i&lt;NUM_PINS+1;i++) {\n     pinMode(allPins[i], OUTPUT);\n  }\n  all_pins_off();\n}\n\nvoid all_pins_off() {\n  for (int i=0;i&lt;NUM_PINS+1;i++) {\n    digitalWrite(allPins[i], HIGH); \n  }\n}\n\nint *currentStep;\nvoid loop() {\n  \n currentStep = STEPPER_SEQUENCE[stepNum];\n  for (int i=0;i&lt;NUM_PINS+1;i++) {\n     if (currentStep[i] == 1) {\n        digitalWrite(allPins[i], HIGH);\n     }\n     else {\n        digitalWrite(allPins[i], LOW);\n     }\n  }\n  delay(5);\n  stepNum +=2;  \/\/ double-stepping. Faster and shakier. \n  stepNum %= NUM_STEPS;\n}<\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">Timers, user input, and the accelerating motor<\/h4>\n\n\n\n<p>Now for the real deal, let&#8217;s code up what we need to do to correct the tangent error and have other practicalities. I was using barebones code for testing and realized it was a pain to reverse my motor (I had to remove wires and rails and twist it by hand. So I decided to add a button to control reversing and stuff.<\/p>\n\n\n\n<p>The motor starts up when you power it up and does its thing. If you press the button once, it reverses all the way back to exactly the starting point (it <em>remembers<\/em>!), then it stops. If you press the button again, it starts back up.<\/p>\n\n\n\n<p>The delay call in the code above is fine and all, but when the loop step is more complicated (i.e. computing our changing rate), there&#8217;s a chance we will not get the right timing. For these kinds of things, there are<a href=\"https:\/\/hackaday.io\/project\/12818-not-grbl\/log\/47580-esp8266-timer0-and-isr\"> timers and interrupts. <\/a> Basically, you write a callback function and register it to be called whenever a specific timer times out. Check it out: (update: this code is <a href=\"https:\/\/github.com\/partofthething\/startracker\">available on github<\/a>)<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">#define NUM_PINS 4\n#define NUM_STEPS 8\n#define RADS_PER_SEC 7.292115e-05\n#define LENGTH_CM 29.113 \/\/ fill in with precise measured value\n#define THETA0 0.0241218 \/\/ fill in with angle at fully closed position (radians)\n#define ROTATIONS_PER_CM 7.8740157  \/\/ 1\/4-20 thread\n#define DOUBLESTEPS_PER_ROTATION 2048.0  \n#define CYCLES_PER_SECOND 80000000\n\n\/\/modes\n#define NORMAL 0\n#define REWINDING 1\n#define STOPPED 2\n\n\nint allPins[NUM_PINS] = {D1, D2, D3, D4};\nint MODE_PIN = D7;\n\n\/\/ from manufacturers datasheet\nint STEPPER_SEQUENCE[NUM_STEPS][NUM_PINS] = {{1,0,0,1},\n                                             {1,0,0,0},\n                                             {1,1,0,0},\n                                             {0,1,0,0},\n                                             {0,1,1,0},\n                                             {0,0,1,0},\n                                             {0,0,1,1},\n                                             {0,0,0,1}};\n\nint step_delta; \nint step_num = 0;\ndouble total_seconds = 0.0;\nlong totalsteps = 0;\ndouble step_interval_s=0.001;\nint *current_step;\nvolatile unsigned long next; \/\/ next time to trigger callback\nvolatile unsigned long now; \/\/ volatile keyword required when things change in callbacks\nvolatile unsigned long last_toggle; \/\/ for debounce\nvolatile short current_mode=NORMAL;\nbool autostop=true;  \/\/ hack for allowing manual rewind at boot\n\nfloat ypt(float ts) {\n   \/\/ bolt insertion rate in cm\/s: y'(t) \n   \/\/ Note, if you run this for ~359 minutes, it goes to infinity!!\n   return LENGTH_CM * RADS_PER_SEC\/pow(cos(THETA0 + RADS_PER_SEC * ts),2);\n}\n\nvoid inline step_motor(void) {\n  \/*  This is the callback function that gets called when the timer\n   *  expires. It moves the motor, updates lists, recomputes\n   *  the step interval based on the current tangent error, \n   *  and sets a new timer. \n   *\/\n  switch(current_mode) {\n    case NORMAL:\n      step_interval_s = 1.0\/(ROTATIONS_PER_CM * ypt(total_seconds)* 2 * DOUBLESTEPS_PER_ROTATION);\n      step_delta = 1;  \/\/ single steps while filming for smoothest operation and highest torque\n      step_num %= NUM_STEPS;\n      break;\n    case REWINDING:\n      \/\/ fast rewind\n      step_interval_s = 0.0025;  \/\/ can often get 2ms but gets stuck sometimes.\n      step_delta = -2;  \/\/ double steps going backwards for speed.\n      if (step_num&lt;0) {\n        step_num+=NUM_STEPS;  \/\/ modulus works here in Python it goes negative in C. \n      }\n      break;\n    case STOPPED:\n      step_interval_s = 0.2;  \/\/ wait a bit to conserve power. \n      break;\n  }\n\n  if (current_mode!=STOPPED) {\n    total_seconds += step_interval_s; \/\/ required for tangent error\n    current_step = STEPPER_SEQUENCE[step_num];\n    do_step(current_step);\n    step_num += step_delta;  \/\/ double-steppin' \n    totalsteps += step_delta;\n  }\n  \n  \/\/ Serial.println(totalsteps);\n  \/\/ Before setting the next timer, subtract out however many\n  \/\/ clock cycles were burned doing all the work above. \n  now = ESP.getCycleCount();\n  next = now + step_interval_s * CYCLES_PER_SECOND - (now-next); \/\/ will auto-rollover.\n  timer0_write(next);  \/\/ see you next time!\n}\n\nvoid do_step(int *current_step) {\n   \/* apply a single step of the stepper motor on its pins. *\/\n   for (int i=0;i&lt;NUM_PINS+1;i++) {\n     if (current_step[i] == 1) {\n        digitalWrite(allPins[i], HIGH);\n     }\n     else {\n        digitalWrite(allPins[i], LOW);\n     }\n   }\n}\n\nvoid setup() {\n  Serial.begin(115200);\n  setup_gpio();\n  setup_timer();\n\n\n  \/\/ Convenient Feature: Hold button down during boot to do a manual rewind.\n  \/\/ Press button again to set new zero point. \n  int buttonUp = digitalRead(MODE_PIN);\n  if(not buttonUp) {\n    Serial.println(\"Manual REWIND!\");\n    autostop=false;\n    current_mode=REWINDING;\n  }\n}\n\nvoid setup_timer() {\n  noInterrupts();\n  timer0_isr_init();\n  timer0_attachInterrupt(step_motor);  \/\/ call this function when timer expires\n  next=ESP.getCycleCount()+1000;\n  timer0_write(next);  \/\/ do first call in 1000 clock cycles. \n  interrupts();\n}\n\nvoid setup_gpio() {\n  \n  for (int i=0;i&lt;NUM_PINS+1;i++) {\n     pinMode(allPins[i], OUTPUT);\n  }\n  all_pins_off();\n  \n  \/\/ Setup toggle button for some user input. \n  pinMode(MODE_PIN, INPUT_PULLUP);\n  attachInterrupt(digitalPinToInterrupt(MODE_PIN), toggle_mode, FALLING);\n}\n\nvoid all_pins_off() {\n  for (int i=0;i&lt;NUM_PINS+1;i++) {\n    digitalWrite(allPins[i], LOW); \n  }\n}\n\nvoid toggle_mode() {\n  \/*  We have several modes that we can toggle between with a button, \n   *  NORMAL, REWIND, and STOPPED.\n   *\/\n  if(ESP.getCycleCount() - last_toggle &lt; 0.2*CYCLES_PER_SECOND) \/\/debounce\n  {\n    return; \n  }\n  if (current_mode == REWINDING){\n    Serial.println(\"STOPPING\");\n    current_mode = STOPPED;\n    all_pins_off();\n    if (not autostop) {\n      \/\/ Reset things after a manual rewind.\n      step_num = 0;\n      total_seconds = 0.0;\n      totalsteps=0;\n      autostop=true;\n    }\n  }\n  else if (current_mode == NORMAL) {\n    Serial.println(\"Rewinding.\");\n    current_mode = REWINDING;\n  }\n  else {\n    Serial.println(\"Restarting.\");\n    current_mode = NORMAL;\n  }\n  last_toggle = ESP.getCycleCount();\n}\n\n\nvoid loop() {\n\n  if(current_mode == REWINDING) {\n      \/\/ we've reached the starting point. stop rewinding.\n      if(totalsteps &lt; 1 and autostop==true){\n        Serial.println(\"Ending the rewind and stopping.\");\n        current_mode=STOPPED;\n        all_pins_off();\n      }\n  }\n  else {\n    \/\/ no-op. just wait for interrupts. \n    yield();  \n  }\n}\n\n\n<\/pre>\n\n\n\n<p>(That bit about setting the timer at the end of the callback took some trial-and-error&#8230; it kept crashing around 4.2 billion clock cycles. Turns out that&#8217;s the max unsigned long int value. Neat. )<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Portable use and Power Consumption<\/h2>\n\n\n\n<p>Stars are better when it&#8217;s darker, and that often requires remote operation. This thing runs off a USB port so if you have one of those external batteries, that should work. I tried a few options here, including a big deep-cycle battery that I use for ham radio for the really long hauls.<\/p>\n\n\n\n<p>Through a 12V-to-5V converter, I measured 0.189 Amps, yielding 2.27 Watts. So my huge 20Ah battery could run it for 4 full days. More practically, 3 of those common lightweight Li-ion 18650s chained together would run it for over 2 days (wow). So that&#8217;s nice.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Polar alignment<\/h2>\n\n\n\n<p>It&#8217;s important to get everything aligned so it works right. You <a href=\"http:\/\/www.astronomyboy.com\/barndoor\/platform.shtml\">have to make sure<\/a>:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Your finderscope is aligned with the hinge axis (swing it and make sure a faraway thing like a star stays in the middle).<\/li>\n\n\n\n<li>Your finderscope is pointed at the celestial pole (close to Polaris, but not right on it)<\/li>\n<\/ul>\n\n\n\n<p>I set a straw on my hinge and aimed it at a tower crane in the distance. Then I pointed the camera at the same crane and rotated it. With some adjustment it didn&#8217;t rotate too much, so at that point I could point the camera at Polaris, lock in the tripod, and be off.<\/p>\n\n\n\n<p>For the longer term, I ordered one of those red-dot things from the internets because my dad got one years ago for his telescope and it was great. Once get it aligned I can just point-and-go.<\/p>\n\n\n\n<p>UPDATE: I got it (see picture in angular calibration section below) and it is perfect.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">First attempts at using<\/h2>\n\n\n\n<p>I tried it out with the straw-on-the-hinge alignment and got it sort-of aligned, to the point that I couldn&#8217;t wait to turn it on and try it. This was from downtown downtown Seattle so the light pollution was pretty intense. Fortunately I shot in RAW and could get some stars out of it.<\/p>\n\n\n\n<p>Test number 1: It works!<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter\"><a href=\"https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/before-after_sm.jpg\"><img loading=\"lazy\" decoding=\"async\" width=\"1037\" height=\"512\" src=\"https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/before-after_sm.jpg\" alt=\"\" class=\"wp-image-1356\" srcset=\"https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/before-after_sm.jpg 1037w, https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/before-after_sm-300x148.jpg 300w, https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/before-after_sm-768x379.jpg 768w, https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/before-after_sm-1024x506.jpg 1024w\" sizes=\"auto, (max-width: 1037px) 100vw, 1037px\" \/><\/a><figcaption class=\"wp-element-caption\">With\/without tracker<\/figcaption><\/figure>\n<\/div>\n\n\n<p>Then I just pointed it around and did what I could.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter\"><a href=\"https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/IMG_6570.jpg\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"683\" src=\"https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/IMG_6570-1024x683.jpg\" alt=\"\" class=\"wp-image-1355\" srcset=\"https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/IMG_6570-1024x683.jpg 1024w, https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/IMG_6570-300x200.jpg 300w, https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/IMG_6570-768x512.jpg 768w, https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/IMG_6570.jpg 1400w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><figcaption class=\"wp-element-caption\">Downtown Seattle Stars. No they don&#8217;t look like that with the human eye.<\/figcaption><\/figure>\n<\/div>\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter\"><a href=\"https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/IMG_6554.jpg\"><img loading=\"lazy\" decoding=\"async\" width=\"520\" height=\"347\" src=\"https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/IMG_6554.jpg\" alt=\"\" class=\"wp-image-1353\" srcset=\"https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/IMG_6554.jpg 520w, https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/IMG_6554-300x200.jpg 300w\" sizes=\"auto, (max-width: 520px) 100vw, 520px\" \/><\/a><figcaption class=\"wp-element-caption\">Jupiter and moons<\/figcaption><\/figure>\n<\/div>\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter\"><a href=\"https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/IMG_6565.jpg\"><img loading=\"lazy\" decoding=\"async\" width=\"1006\" height=\"672\" src=\"https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/IMG_6565.jpg\" alt=\"\" class=\"wp-image-1354\" srcset=\"https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/IMG_6565.jpg 1006w, https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/IMG_6565-300x200.jpg 300w, https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/IMG_6565-768x513.jpg 768w\" sizes=\"auto, (max-width: 1006px) 100vw, 1006px\" \/><\/a><figcaption class=\"wp-element-caption\">Our Moon<\/figcaption><\/figure>\n<\/div>\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter\"><a href=\"https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/seattle-with-stars-sm.jpg\"><img loading=\"lazy\" decoding=\"async\" width=\"1222\" height=\"959\" src=\"https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/seattle-with-stars-sm.jpg\" alt=\"\" class=\"wp-image-1352\" srcset=\"https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/seattle-with-stars-sm.jpg 1222w, https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/seattle-with-stars-sm-300x235.jpg 300w, https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/seattle-with-stars-sm-768x603.jpg 768w, https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/seattle-with-stars-sm-1024x804.jpg 1024w\" sizes=\"auto, (max-width: 1222px) 100vw, 1222px\" \/><\/a><figcaption class=\"wp-element-caption\">Seattle under the stars<\/figcaption><\/figure>\n<\/div>\n\n\n<p>Not bad. It wasn&#8217;t perfectly aligned so I&#8217;ll do more serious tests once I get that done and head to some actual dark sky.<\/p>\n\n\n\n<p><iframe loading=\"lazy\" width=\"560\" height=\"315\" src=\"https:\/\/www.youtube.com\/embed\/jlbfUT0j1wI\" allowfullscreen=\"allowfullscreen\" frameborder=\"0\"><\/iframe><\/p>\n\n\n\n<h3 class=\"wp-block-heading\">UPDATE 2017 Eclipse in Oregon<\/h3>\n\n\n\n<p>I used it on the eclipse and it worked great. It was nice to have the camera clicking away with exposure bracketing so I could watch the eclipse myself. I&#8217;m really happy with the result.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter\"><a href=\"https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/eclipse-blend-1.jpg\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"684\" src=\"https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/eclipse-blend-1-1024x684.jpg\" alt=\"Picture of the eclipse\" class=\"wp-image-2034\" srcset=\"https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/eclipse-blend-1-1024x684.jpg 1024w, https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/eclipse-blend-1-300x200.jpg 300w, https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/eclipse-blend-1-768x513.jpg 768w, https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/eclipse-blend-1-1536x1026.jpg 1536w, https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/eclipse-blend-1.jpg 2048w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><figcaption class=\"wp-element-caption\">Eclipse photo that&#8217;s a blend of a few photos taken automatically using the star-tracker. Turns out the sun is a star too!<\/figcaption><\/figure>\n<\/div>\n\n\n<h3 class=\"wp-block-heading\">UPDATE 2020 Use with Neowise<\/h3>\n\n\n\n<p>I used it on C\/2020 NEOWISE F3 comet and it worked amazingly well.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter\"><a href=\"https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/comet.jpg\"><img loading=\"lazy\" decoding=\"async\" width=\"683\" height=\"1024\" src=\"https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/comet-683x1024.jpg\" alt=\"C\/2020 NEOWISE F3 Comet\" class=\"wp-image-1994\" srcset=\"https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/comet-683x1024.jpg 683w, https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/comet-200x300.jpg 200w, https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/comet-768x1152.jpg 768w, https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/comet-1024x1536.jpg 1024w, https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/comet.jpg 1365w\" sizes=\"auto, (max-width: 683px) 100vw, 683px\" \/><\/a><figcaption class=\"wp-element-caption\">C\/2020 NEOWISE F3 20 second exposure on barn-door tracker<\/figcaption><\/figure>\n<\/div>\n\n\n<h3 class=\"wp-block-heading\">UPDATE 2024: Eclipse in Missouri<\/h3>\n\n\n\n<p>It worked again in Missouri!<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"683\" src=\"https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/IMG_2175-1024x683.jpg\" alt=\"\" class=\"wp-image-2499\" srcset=\"https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/IMG_2175-1024x683.jpg 1024w, https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/IMG_2175-300x200.jpg 300w, https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/IMG_2175-768x512.jpg 768w, https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/IMG_2175-1536x1024.jpg 1536w, https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/IMG_2175.jpg 2048w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Precise Angular Velocity Calibration<\/h2>\n\n\n\n<p>Just to check my math a bit more, I <a href=\"https:\/\/partofthething.com\/thoughts\/?p=1367\">got a digital level and figured out how to get it to dump data to my PC. <\/a>Then I let it run with my tracker for a while and did some least-squares fitting to see how it was working.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter\"><a href=\"https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/calibrating_with_level.jpg\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"576\" src=\"https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/calibrating_with_level-1024x576.jpg\" alt=\"Pic of level sitting on tracker\" class=\"wp-image-1379\" srcset=\"https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/calibrating_with_level-1024x576.jpg 1024w, https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/calibrating_with_level-300x169.jpg 300w, https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/calibrating_with_level-768x432.jpg 768w, https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/calibrating_with_level.jpg 2048w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><figcaption class=\"wp-element-caption\">Calibrating the barn-door tracker with a digital level. Note also the red-dot sight for alignment.<\/figcaption><\/figure>\n<\/div>\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter\"><a href=\"https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/calibration_with_fit.png\"><img loading=\"lazy\" decoding=\"async\" width=\"711\" height=\"555\" src=\"https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/calibration_with_fit.png\" alt=\"Graph of data with line through dots and equation\" class=\"wp-image-1380\" srcset=\"https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/calibration_with_fit.png 711w, https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/calibration_with_fit-300x234.png 300w\" sizes=\"auto, (max-width: 711px) 100vw, 711px\" \/><\/a><figcaption class=\"wp-element-caption\">Acquired data with least-squares linear fit<\/figcaption><\/figure>\n<\/div>\n\n\n<p>So I measured a nice and constant 7.255e-5 radians\/second over 10 minutes. That&#8217;s within 0.5% of the right answer. I can now adjust my target in software to speed it up by 0.5% and get it really accurate! The calibration software is <a href=\"https:\/\/github.com\/partofthething\/startracker\">also available on github. <\/a><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Future work<\/h2>\n\n\n\n<p>Get a telescope. Learn how to stack photos.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Some references<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/github.com\/esp8266\/Arduino\/blob\/master\/variants\/d1_mini\/pins_arduino.h#L49-L61\">D1 Wemos Pinout for esp8266<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/hackaday.io\/project\/12818-not-grbl\/log\/47580-esp8266-timer0-and-isr\">Info on timers on the ESP8266<\/a><\/li>\n\n\n\n<li><a href=\"http:\/\/www.switchdoc.com\/2015\/10\/iot-esp8266-timer-tutorial-arduino-ide\/\">Some info on ESP8266 software timers that I didn&#8217;t use<\/a><\/li>\n\n\n\n<li><a href=\"http:\/\/www.raspberrypi-spy.co.uk\/2012\/07\/stepper-motor-control-in-python\/\">Info on controlling these stepper motors from code<\/a><\/li>\n\n\n\n<li><a href=\"http:\/\/www.astronomyboy.com\/barndoor\/platform.shtml\">Info on celestial alignment of trackers<\/a><\/li>\n\n\n\n<li>All the people on <a href=\"https:\/\/www.reddit.com\/r\/astrophotography\">\/r\/astrophotography <\/a>who made things like this before<\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>I like to mix hobbies, so naturally I&#8217;ve been eying astrophotography for a while. I&#8217;ve taken a time-lapse here and a moon picture there but, inspired by the folks over at \/r\/astrophotography,&nbsp; I wanted to take it to the next level. Since the Earth is spinning, any long exposure of the night sky has star &hellip; <a href=\"https:\/\/partofthething.com\/thoughts\/making-a-cheap-and-simple-barn-door-star-tracker-with-software-tangent-correction-for-astrophotography\/\" class=\"more-link\">Continue reading <span class=\"screen-reader-text\">Making a cheap and simple barn-door star tracker with software tangent correction for astrophotography<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"activitypub_content_warning":"","activitypub_content_visibility":"","activitypub_max_image_attachments":4,"activitypub_interaction_policy_quote":"anyone","activitypub_status":"federated","footnotes":""},"categories":[3,69,72],"tags":[79,78],"class_list":["post-1327","post","type-post","status-publish","format-standard","hentry","category-computers","category-electronics-and-physics","category-photography","tag-define","tag-ffffe0"],"_links":{"self":[{"href":"https:\/\/partofthething.com\/thoughts\/wp-json\/wp\/v2\/posts\/1327","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/partofthething.com\/thoughts\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/partofthething.com\/thoughts\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/partofthething.com\/thoughts\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/partofthething.com\/thoughts\/wp-json\/wp\/v2\/comments?post=1327"}],"version-history":[{"count":25,"href":"https:\/\/partofthething.com\/thoughts\/wp-json\/wp\/v2\/posts\/1327\/revisions"}],"predecessor-version":[{"id":2500,"href":"https:\/\/partofthething.com\/thoughts\/wp-json\/wp\/v2\/posts\/1327\/revisions\/2500"}],"wp:attachment":[{"href":"https:\/\/partofthething.com\/thoughts\/wp-json\/wp\/v2\/media?parent=1327"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/partofthething.com\/thoughts\/wp-json\/wp\/v2\/categories?post=1327"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/partofthething.com\/thoughts\/wp-json\/wp\/v2\/tags?post=1327"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}