Skip to content

Instantly share code, notes, and snippets.

@MattBlack85
Last active September 16, 2021 15:00
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save MattBlack85/0465b12a61e868ede172b658c7a229b5 to your computer and use it in GitHub Desktop.
Save MattBlack85/0465b12a61e868ede172b658c7a229b5 to your computer and use it in GitHub Desktop.
// 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