Last active
September 16, 2021 15:00
-
-
Save MattBlack85/0465b12a61e868ede172b658c7a229b5 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Moonlite-compatible stepper controller | |
// Written by George Carlson June 2014. | |
// This version uses the Tiny 2.0 a uController based on the Atmel ATMEGA32U4. | |
// hardware for the remote hand control is not supported. | |
// | |
// Many thanks to orly.andico@gmail.com, for the original command parser, others for bits on code picked up here and there on the net | |
// 28BYJ | |
// Since the MoonLite focuser only works with positive integers, I center (zero) my system at 30000. The range is from | |
// 0 to 65535, so 30000 is a good round number for the center. | |
// If the Current Position, or New Position is set to 0, this code will set the values at 30000. The reason for this | |
// is the system is designed to be set at center, manually focused, then focused in a +/- fashion by the controller. | |
// Thanks to George, this code has been found https://www.cloudynights.com/topic/466453-super-compact-electronic-focuser/ | |
// and it's a slightly modified version that is able to read the temperature from a DSXXXX sensor and present it to moonlite | |
// compatible firmwares, this version needs the OneWire library to work properly. | |
// Please note that the data pin is hooked up to arduino digital pin 2 by default | |
#include <OneWire.h> | |
#define RUNLED 11 /* Amber LED lights whenever motor is active, 11 for the Teensy */ | |
#define HOME 30000 | |
#define MAXCOMMAND 8 | |
char inChar; | |
char cmd[MAXCOMMAND]; | |
char param[MAXCOMMAND]; | |
char line[MAXCOMMAND]; | |
char tempString[6]; | |
unsigned int hashCmd; | |
long pos; | |
int isRunning = 0; | |
int speed = 2; | |
int eoc = 0; | |
int idx = 0; | |
long millisLastMove = 0; | |
int Current = HOME; | |
int Target = HOME; | |
int DistanceToGo = 0; | |
int minSpeed = 2; | |
int maxSpeed = 20; | |
int testPin = 12; | |
// Temperature measurement | |
OneWire ds(2); // on pin 2 (a 4.7K resistor is necessary) | |
byte i; | |
byte present = 0; | |
byte type_s; | |
byte data[12]; | |
byte addr[8]; | |
float temperature; | |
float tempTemp; | |
// Remote hand controller NOT USED ON TEENSY VERSION (but could be) | |
int handController = 1; | |
unsigned int rADC; | |
int speedTable[16] = {2, 2, 4, 8, 10, 20, 0, 0, 0, 0, 20, 10, 8, 4, 2, 2}; | |
int goTable[16] = { -1, -1, -1, -1, -1, -1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1}; | |
// Motor connections | |
int motorPin1 = 7; // Blue - 28BYJ48 pin 1 | |
int motorPin2 = 6; // Pink - 28BYJ48 pin 2 | |
int motorPin3 = 5; // Yellow - 28BYJ48 pin 3 | |
int motorPin4 = 4; // Orange - 28BYJ48 pin 4 | |
// Red - 28BYJ48 pin 5 (VCC) | |
// lookup table for motor phase control | |
int StepTable[8] = {0b01001, 0b00001, 0b00011, 0b00010, 0b00110, 0b00100, 0b01100, 0b01000}; | |
int phase = 0; | |
void forwardstep() { | |
Current++; | |
if (++phase > 7) phase = 0; | |
//stepper1.move(1); | |
setOutput(phase); | |
for (int i = 0; i < speed >> 1; i++) { | |
delay(1); | |
} | |
} | |
void backwardstep() | |
{ | |
Current--; | |
if (--phase < 0) phase = 7; | |
setOutput(phase); | |
for (int i = 0; i < speed >> 1; i++) { | |
delay(1); | |
} | |
} | |
void setOutput(int out) | |
{ | |
digitalWrite(motorPin1, bitRead(StepTable[out], 0)); | |
digitalWrite(motorPin2, bitRead(StepTable[out], 1)); | |
digitalWrite(motorPin3, bitRead(StepTable[out], 2)); | |
digitalWrite(motorPin4, bitRead(StepTable[out], 3)); | |
} | |
int GetTemp(void) | |
{ | |
ds.search(addr); | |
if (OneWire::crc8(addr, 7) != addr[7]) { | |
return; | |
} | |
// the first ROM byte indicates which chip | |
switch (addr[0]) { | |
case 0x10: | |
type_s = 1; | |
break; | |
case 0x28: | |
type_s = 0; | |
break; | |
case 0x22: | |
type_s = 0; | |
break; | |
default: | |
return; | |
} | |
ds.reset(); | |
ds.select(addr); | |
ds.write(0x44, 1); // start conversion, with parasite power on at the end | |
delay(1000); // maybe 750ms is enough, maybe not | |
// we might do a ds.depower() here, but the reset will take care of it. | |
present = ds.reset(); | |
ds.select(addr); | |
ds.write(0xBE); // Read Scratchpad | |
for ( i = 0; i < 9; i++) { // we need 9 bytes | |
data[i] = ds.read(); | |
} | |
// Convert the data to actual temperature | |
// because the result is a 16 bit signed integer, it should | |
// be stored to an "int16_t" type, which is always 16 bits | |
// even when compiled on a 32 bit processor. | |
int16_t raw = (data[1] << 8) | data[0]; | |
if (type_s) { | |
raw = raw << 3; // 9 bit resolution default | |
if (data[7] == 0x10) { | |
// "count remain" gives full 12 bit resolution | |
raw = (raw & 0xFFF0) + 12 - data[6]; | |
} | |
} else { | |
byte cfg = (data[4] & 0x60); | |
// at lower res, the low bits are undefined, so let's zero them | |
if (cfg == 0x00) raw = raw & ~7; // 9 bit resolution, 93.75 ms | |
else if (cfg == 0x20) raw = raw & ~3; // 10 bit res, 187.5 ms | |
else if (cfg == 0x40) raw = raw & ~1; // 11 bit res, 375 ms | |
//// default is 12 bit resolution, 750 ms conversion time | |
} | |
temperature = (float)raw / 16.0; | |
Serial.print(temperature); | |
return temperature; | |
} | |
void readHandController() | |
{ | |
//digitalWrite(testPin,LOW); | |
//rADC = analogRead(handController); /* read twice, keep second reading */ | |
rADC = analogRead(handController); | |
analogReference(DEFAULT); | |
if (rADC > 20) | |
// if hand controller is connected | |
{ | |
rADC = rADC >> 6; | |
if (goTable[rADC]) | |
{ | |
speed = speedTable[rADC]; | |
Target = Target + goTable[rADC]; | |
isRunning = 1; | |
//digitalWrite(RUNLED,HIGH); | |
} | |
} | |
// digitalWrite(testPin,HIGH); | |
} | |
long hexstr2long(char *line) { | |
long ret = 0; | |
ret = strtol(line, NULL, 16); | |
return (ret); | |
} | |
////////////////////////////////////////////////////////////////////////////////////////////////////// | |
// start of program | |
void setup() | |
{ | |
Serial.begin(9600); | |
//setup the motor pins as outputs | |
pinMode(motorPin1, OUTPUT); | |
pinMode(motorPin2, OUTPUT); | |
pinMode(motorPin3, OUTPUT); | |
pinMode(motorPin4, OUTPUT); | |
analogReference(DEFAULT); | |
memset(line, 0, MAXCOMMAND); | |
millisLastMove = millis(); | |
} | |
// Forever Loop | |
void loop() { | |
DistanceToGo = Target - Current; /* compute remaining distance to go */ | |
if (!Serial.available()) { | |
// run the stepper if there's no pending command and if there are pending movements | |
if (isRunning) { | |
if (DistanceToGo > 0) | |
forwardstep(); | |
if (DistanceToGo < 0) | |
backwardstep(); | |
millisLastMove = millis(); /* reset idle timer */ | |
} | |
else { /* Check to see if idle time is up */ | |
if ((millis() - millisLastMove) > 5000) { | |
// if so, turn off motor | |
digitalWrite(motorPin1, 0); | |
digitalWrite(motorPin2, 0); | |
digitalWrite(motorPin3, 0); | |
digitalWrite(motorPin4, 0); | |
} | |
} | |
if (DistanceToGo == 0) { | |
// if motion is complete | |
//digitalWrite(RUNLED,LOW); | |
isRunning = 0; | |
} | |
} | |
else { | |
// read the command until the terminating # character | |
while (Serial.available() && !eoc) { | |
inChar = Serial.read(); | |
if (inChar != '#' && inChar != ':') { | |
line[idx++] = inChar; | |
if (idx >= MAXCOMMAND) | |
idx = MAXCOMMAND - 1; | |
} | |
else { | |
if (inChar == '#') | |
eoc = 1; | |
} | |
} | |
} // end if (!Serial.available()) | |
// process the command we got | |
if (eoc) { | |
//digitalWrite(testPin,LOW); | |
memset(cmd, 0, MAXCOMMAND); | |
memset(param, 0, MAXCOMMAND); | |
int len = strlen(line); | |
if (len >= 2) | |
strncpy(cmd, line, 2); | |
if (len > 2) | |
strncpy(param, line + 2, len - 2); | |
memset(line, 0, MAXCOMMAND); | |
eoc = 0; | |
idx = 0; | |
// the stand-alone program sends :C# :GB# on startup | |
// :C# is to start a temperature conversion, doesn't require any response (we don't use it) | |
hashCmd = (byte(cmd[0]) | (byte(cmd[1]) << 8)); /* combine the two command charaters into an unsigned int */ | |
switch (hashCmd) { | |
// GP command Get current position | |
case ('P'<<8 | 'G'): | |
pos = Current; | |
sprintf(tempString, "%04X", pos); | |
Serial.print(tempString); | |
Serial.print("#"); | |
break; | |
case ('T'<<8 | 'G'): | |
// GT command Get Temperature | |
tempTemp = GetTemp(); | |
sprintf(tempString, "%04X", tempTemp); | |
Serial.print(tempString); | |
Serial.print("#"); | |
break; | |
case ('I'<<8 | 'G'): | |
// GI command 01 if motor running, 00 if not | |
if (DistanceToGo != 0) | |
Serial.print("01#"); | |
else | |
Serial.print("00#"); | |
break; | |
case ('B'<<8 | 'G'): | |
// GB command Get current backlight value, always 00 | |
Serial.print("00#"); | |
break; | |
case ('H'<<8 | 'P'): | |
// PH command Find motor home | |
Current = HOME; | |
isRunning = 1; | |
//digitalWrite(RUNLED,HIGH); | |
break; | |
case ('V'<<8 | 'G'): | |
// GV command Get software version, always 10 | |
Serial.print("10#"); | |
break; | |
case ('N'<<8 | 'G'): | |
// GN command Get new (target) position | |
pos = Target; | |
sprintf(tempString, "%04X", pos); | |
Serial.print(tempString); | |
Serial.print("#"); | |
break; | |
case ('C'<<8 | 'G'): | |
// GC command Get temerature coefficient, always 2 | |
Serial.print("02#"); | |
break; | |
case ('D'<<8 | 'G'): | |
// GD command Get motor speed | |
sprintf(tempString, "%02X", speed); | |
Serial.print(tempString); | |
Serial.print("#"); | |
break; | |
case ('D'<<8 | 'S'): | |
// SD command Set motor speed | |
speed = hexstr2long(param); | |
if (speed < minSpeed) | |
speed = minSpeed; | |
if (speed > maxSpeed) | |
speed = maxSpeed; | |
break; | |
case ('H'<<8 | 'G'): | |
// GH command Get half step mode, always 00 | |
Serial.print("00#"); | |
break; | |
case ('P'<<8 | 'S'): | |
// SP command Set current position | |
pos = hexstr2long(param); | |
if (pos == 0) | |
pos = HOME; | |
Current = pos; | |
break; | |
case ('N'<<8 | 'S'): | |
// SN command Set new position | |
pos = hexstr2long(param); | |
if (pos == 0) | |
pos = HOME; | |
Target = pos; | |
break; | |
case ('G'<<8 | 'F'): | |
// FG command Start motor command | |
isRunning = 1; | |
//digitalWrite(RUNLED,HIGH); | |
break; | |
case ('Q'<<8 | 'F'): | |
// FQ command Stop motor command | |
isRunning = 0; | |
//digitalWrite(RUNLED,LOW); | |
break; | |
} | |
//digitalWrite(testPin,HIGH); | |
} // end process command | |
} // end forever loop |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment