Quite some time has passed since I automated my manual filter wheel, so now I write the article. I built this one mainly for fun, I could afford the (overpriced) automated version.

The specifications behind the whole idea

  • automate the manual filterwheel — without modifying the wheel itself, revert the changes should I get bored or if anything goes south
  • make it fail gracefully: the DC motor can be driven by anything outputting around 5V, a manual H-bridge and a couple of AAA batteries for example. Experience: the first version with the encoder failed due to the intense sunlight — hence duct tape and a paper cover of the window were added. Failing gracefully meant disconnecting the logic and connecting the manual H-bridge. No observation wasted.
  • make it compatible with my existing gear (the soapbox and the noszogtató)

Block diagrams

 

 

Photos

Filters in the wheel, further pieces on the table

Filters in the wheel, further pieces on the table

The encoder module going into the wheel

The encoder module going into the wheel

The encoder module in place, and a jack for driving the DC motor through an H-bridge

The encoder module in place, and a jack for driving the DC motor through an H-bridge

The filterwheel, the driving wheel and the encoder

The filterwheel, the driving wheel and the encoder

The encoder works

The encoder works

No encoder yet, just the H-bridge

No encoder yet, just the H-bridge

No encoder yet

No encoder yet

Observing Venus

Observing Venus

selfie, H-bridge

selfie, H-bridge

The hand controller unit

The hand controller unit

Observing Venus

Observing Venus

Bottle-optics

Bottle-optics

Day mode only

Day mode only

Day mode

Day mode

Night mode (switch on the left of the LCD). On the left: the soapbox

Night mode (switch on the left of the LCD). On the left: the soapbox

On the big tube, the numbers are covered

On the big tube, the numbers are covered

Uncovered numbers

Uncovered numbers

The balcony

The balcony

Motor+encoder, power through microUSB, control through daisy chained to the soapbox

Motor+encoder, power through microUSB, control through daisy chained to the soapbox

Desktop demo (English)

Field Demo, Observing Venus (Hungarian)

Source code

#define FILTERWHEEL_WELCOME_SCREEN_FIRST_LINE  "FilterWheel v0.4"
#define FILTERWHEEL_WELCOME_SCREEN_SECOND_LINE " csillagtura.ro "


//#define FILTERWHEEL_PROJECT_DEBUG_MODE





#include <AT24Cxx.h>

#include <Wire.h> 
#include <LiquidCrystal_I2C.h>

#define EEPROM_I2C_ADDRESS 0x50

AT24Cxx eep(EEPROM_I2C_ADDRESS, 32);


LiquidCrystal_I2C lcd(0x27,16,2);  // set the LCD address to 0x27 for a 16 chars and 2 line display

//=================== PINS ======================

#define PIN_MOTOR_RIGHT A0
#define PIN_MOTOR_LEFT A1


#define PIN_ROW_0 12
#define PIN_ROW_1 11
#define PIN_ROW_2 10
#define PIN_ROW_3  9

#define PIN_COL_0 8
#define PIN_COL_1 7
#define PIN_COL_2 6
#define PIN_COL_3 5

#define PIN_OPTO_ENABLE 4

#define PIN_OPTO_INPUT A2

#define PIN_MYSERIAL_TX 3
#define PIN_MYSERIAL_RX 2






//====================== KEYBOARD ==========================
#define KEYBOARD_INPUT_LEN 10
char keyboardInput[KEYBOARD_INPUT_LEN];
int keyboardCursorRead = 0;
int keyboardCursorWrite = 0;
int keyboardCurrentColumn = 0;
const char keyboardKeys[16] = {'1','2','3','A','4','5','6','B','7','8','9','C','*','0','#','D'};

char currentKey = 0;



char keyboardCandidate = 0;
char keyboardPreviousCandidate = 0;
int keyboardCandidateObservedCount = 0;




void keyboardInsert(char c){
  int prev = keyboardCursorWrite - 1;
  if (prev == -1){
    prev = KEYBOARD_INPUT_LEN - 1;
  }
  if (keyboardInput[prev] == c){
    return ;
  }
  keyboardInput[keyboardCursorWrite] = c;
  keyboardCursorWrite++;
  if (keyboardCursorWrite >= KEYBOARD_INPUT_LEN ){
    keyboardCursorWrite = 0;
  }
}

void keyboardInit(){
  pinMode(PIN_ROW_0, INPUT_PULLUP);
  pinMode(PIN_ROW_1, INPUT_PULLUP);
  pinMode(PIN_ROW_2, INPUT_PULLUP);
  pinMode(PIN_ROW_3, INPUT_PULLUP);

  pinMode(PIN_COL_0, OUTPUT);
  pinMode(PIN_COL_1, OUTPUT);
  pinMode(PIN_COL_2, OUTPUT);
  pinMode(PIN_COL_3, OUTPUT);

  digitalWrite(PIN_COL_0, HIGH);
  digitalWrite(PIN_COL_1, HIGH);
  digitalWrite(PIN_COL_2, HIGH);
  digitalWrite(PIN_COL_3, HIGH);
} 

void keyboardProcess(){
  digitalWrite(PIN_COL_0, keyboardCurrentColumn == 0 ? LOW: HIGH);
  digitalWrite(PIN_COL_1, keyboardCurrentColumn == 1 ? LOW: HIGH);
  digitalWrite(PIN_COL_2, keyboardCurrentColumn == 2 ? LOW: HIGH);
  digitalWrite(PIN_COL_3, keyboardCurrentColumn == 3 ? LOW: HIGH);
  if (keyboardCurrentColumn == 0){
    //full cycle, reset
    keyboardCandidate = 0;
  }
  if (digitalRead(PIN_ROW_0) == LOW){
    keyboardCandidate = keyboardKeys[ 0*4 + keyboardCurrentColumn ];
  }
  if (digitalRead(PIN_ROW_1) == LOW){
    keyboardCandidate = keyboardKeys[ 1*4 + keyboardCurrentColumn ];
  }
  if (digitalRead(PIN_ROW_2) == LOW){
    keyboardCandidate = keyboardKeys[ 2*4 + keyboardCurrentColumn ];
  }
  if (digitalRead(PIN_ROW_3) == LOW){
    keyboardCandidate = keyboardKeys[ 3*4 + keyboardCurrentColumn ];
  }
  
  keyboardCurrentColumn++;
  if (keyboardCurrentColumn > 3){    
    keyboardCurrentColumn = 0;
  }

  if (0 == keyboardCurrentColumn){
    //a full cycle has been done
    if (keyboardCandidate == keyboardPreviousCandidate){
      if (keyboardCandidateObservedCount > 100){
        //we have a keypress
        keyboardInsert(keyboardCandidate);
        keyboardCandidateObservedCount = 0;        
      }else{
        keyboardCandidateObservedCount++;
      }
    }else{
      keyboardCandidateObservedCount = 0;
      keyboardPreviousCandidate = keyboardCandidate;
    }
  }


  //Serial.print(candidate);
}

char keyboardGetCurrentKey(){
  char r = 0;
  if (keyboardCursorRead != keyboardCursorWrite){
    r = keyboardInput[keyboardCursorRead];
    keyboardCursorRead = keyboardCursorWrite;    
  };
  return r;
}



#define OPTO_CHANGE_OBSERVED 1
#define OPTO_NO_CHANGE_OBSERVED 0
    
typedef struct OptoObserver {
  unsigned int changes;
  unsigned int observingSameStateCounter;
  byte observingState;
  byte observingPreviousState;
  byte currentState;
  byte optoIsObserving;  
} OptoObserver;

OptoObserver optoObserver;


void optoStart(){
    digitalWrite(PIN_OPTO_ENABLE, HIGH);
    optoObserver.observingState = (digitalRead(PIN_OPTO_INPUT) == HIGH) ? 1 : 0;
    optoObserver.observingPreviousState = optoObserver.observingState;
    optoObserver.optoIsObserving = 1;
}

void optoStop(){
  optoObserver.optoIsObserving = 0;
  digitalWrite(PIN_OPTO_ENABLE, LOW);
}

void optoInit(){
  pinMode(PIN_OPTO_INPUT, INPUT_PULLUP);
  pinMode(PIN_OPTO_ENABLE, OUTPUT);  
  digitalWrite(PIN_OPTO_ENABLE, LOW);
  optoObserver.optoIsObserving = 0;
}

byte optoObserverProcess(){
  byte rez = OPTO_NO_CHANGE_OBSERVED;
  if (1 == optoObserver.optoIsObserving){
    optoObserver.observingState = (digitalRead(PIN_OPTO_INPUT) == HIGH) ? 1 : 0;
    if (optoObserver.observingState == optoObserver.observingPreviousState){
      optoObserver.observingSameStateCounter++;
      if (optoObserver.observingSameStateCounter > 200){
        optoObserver.observingSameStateCounter = 200;
      } 
      if (optoObserver.observingSameStateCounter > 100){
        if (optoObserver.currentState != optoObserver.observingState){
          optoObserver.currentState = optoObserver.observingState;
          //we have an event here
          optoObserver.changes++;
          rez = OPTO_CHANGE_OBSERVED;
          /*
          lcd.setCursor(0,1);
          char n[10];
          itoa(changes, n, 10);
          lcd.print(n);
          lcd.print("     ");
          */
        }
      } 
    }else{
      optoObserver.observingSameStateCounter = 0;
    }
    optoObserver.observingPreviousState = optoObserver.observingState;
  }else{
    // the opto is turned off
  }
  return rez;
}





inline int chrToDir(char dir){
  if (dir == 'L'){
    return 1;
  }else{
    return -1;
  };
}

void(* resetFunc) (void) = 0; //declare reset function @ address 0



#define NUMBER_1_FILTER_INDEX_CHARACTER '\1'
#define NUMBER_2_FILTER_INDEX_CHARACTER '\2'
#define NUMBER_3_FILTER_INDEX_CHARACTER '\3'
#define NUMBER_4_FILTER_INDEX_CHARACTER '\4'
#define NUMBER_5_FILTER_INDEX_CHARACTER '\5'
#define MOTOR_IS_STATIONARY_CHARACTER   '\6'
#define MOTOR_BACKSLASH_CHARACTER       '\7'


byte customBackslash[8] = {
  0b00000,
  0b10000,
  0b01000,
  0b00100,
  0b00010,
  0b00001,
  0b00000,
  0b00000
};
byte customDot[8] = {
  0b00000,
  0b00000,
  0b00100,
  0b00000,
  0b00000,
  0b00000,
  0b00000,
  0b00000
};
/*
byte customOneFilterIndex[8] = {
  0b01000,
  0b11000,
  0b01001,
  0b01000,
  0b01001,
  0b11100,
  0b00000,
  0b00000
};

byte customTwoFilterIndex[8] = {
  0b01100,
  0b10100,
  0b00101,
  0b01000,
  0b10001,
  0b11100,
  0b00000,
  0b00000
};
*/

byte customOneFilterIndex[8] = {
  0b01000,
  0b11000,
  0b01000,
  0b01000,
  0b11100,
  0b00000,
  0b00000,
  0b00000
};

byte customTwoFilterIndex[8] = {
  0b01000,
  0b10100,
  0b00100,
  0b01000,
  0b11100,
  0b00000,
  0b00000,
  0b00000
};

byte customThreeFilterIndex[8] = {
  0b11000,
  0b00100,
  0b01000,
  0b00100,
  0b11000,
  0b00000,
  0b00000,
  0b00000
};

byte customFourFilterIndex[8] = {
  0b00100,
  0b01100,
  0b10100,
  0b11110,
  0b00100,
  0b00000,
  0b00000,
  0b00000
};

byte customFiveFilterIndex[8] = {
  0b11100,
  0b10000,
  0b11100,
  0b00100,
  0b11000,
  0b00000,
  0b00000,
  0b00000
};

inline void customCharsInit(){
  lcd.createChar((byte) MOTOR_BACKSLASH_CHARACTER, customBackslash);
  lcd.createChar((byte) MOTOR_IS_STATIONARY_CHARACTER, customDot);
  lcd.createChar((byte) NUMBER_1_FILTER_INDEX_CHARACTER, customOneFilterIndex);
  lcd.createChar((byte) NUMBER_2_FILTER_INDEX_CHARACTER, customTwoFilterIndex);
  lcd.createChar((byte) NUMBER_3_FILTER_INDEX_CHARACTER, customThreeFilterIndex);
  lcd.createChar((byte) NUMBER_4_FILTER_INDEX_CHARACTER, customFourFilterIndex);
  lcd.createChar((byte) NUMBER_5_FILTER_INDEX_CHARACTER, customFiveFilterIndex);

}


//=================== MOTOR =====================
const char motorMovingCharacters[4] = {
   '/', '-', MOTOR_BACKSLASH_CHARACTER, '|'
};
int motorMovingCharactersCursor = 0;
int motorIsMoving = 0;
unsigned long int motorOldMillis = 0;




byte motorStoppedFlag = 0;

void motorStop(byte updateScreen){
  optoStop();
  motorIsMoving = 0;
  motorSetDirectionPinsStateByDir(motorIsMoving);
  if (1 == updateScreen){
    motorDrawMovementStatus();
  }
  motorStoppedFlag = 1;
}


void motorInit(){
  pinMode(PIN_MOTOR_LEFT, OUTPUT);
  pinMode(PIN_MOTOR_RIGHT, OUTPUT);
  motorStop(0);
  customCharsInit();
}




#include <SoftwareSerial.h>
SoftwareSerial mySerial(PIN_MYSERIAL_RX, PIN_MYSERIAL_TX);


const int READ_BUFFER_LEN = 96;
char filterWheelSerialReadBuffer[READ_BUFFER_LEN];
char mySerialReadBuffer[READ_BUFFER_LEN];





inline void mySerial_process(){
  while (mySerial.available() > 0){
    char c = mySerial.read();
    if (c == '#'){
      mySerialReadBuffer[0] = 0; 
    }else{
      if (c == '/'){
        Serial.println("message from upstairs");
        Serial.println(mySerialReadBuffer);
        command_process(mySerialReadBuffer, 1);
      }else{
        //continue to concat
        int n = strlen(mySerialReadBuffer);
        if (n < READ_BUFFER_LEN - 2) {
          mySerialReadBuffer[n] = c;
          mySerialReadBuffer[n + 1] = 0;
        }
      }
    }    
  }
}


inline void process_Serial(){
  while (Serial.available() > 0) {
    char c = Serial.read();
    if (c == '#') {
      filterWheelSerialReadBuffer[0] = 0;
    } else {
      if (c == '/') {
        command_process(filterWheelSerialReadBuffer, 0);
        //mySerial.write(filterWheelSerialReadBuffer);
      } else {
        //continue to concat
        int n = strlen(filterWheelSerialReadBuffer);
        if (n < READ_BUFFER_LEN - 2) {
          filterWheelSerialReadBuffer[n] = c;
          filterWheelSerialReadBuffer[n + 1] = 0;
        }
      }
    }
  };
}


inline void serialInit(){
  Serial.begin(9600);
  mySerial.begin(4800);
}

inline void serialProcess(){
  process_Serial();  
  mySerial_process();
}
  
  


void motorProcess(){
  if (0 != motorIsMoving){
    unsigned long int m = millis();
    if (m > motorOldMillis+500){
      motorOldMillis = m;
      motorMovingCharactersCursor++;
      if (motorMovingCharactersCursor >= 4){
        motorMovingCharactersCursor = 0;
      }
      motorDrawMovementStatus();
    }
  }
  if (1 == motorStoppedFlag){
    motorStoppedFlag = 0;
    mySerial.println("#?fw:motstopd?/");
  }
}
void motorStart(int dir){
  if (dir == 0){
    motorIsMoving = 0;
  }else{
    optoStart();
    
    if (dir > 0){
      motorIsMoving = 1;
    }else{
      motorIsMoving = -1;
    };
  }
  motorDrawMovementStatus();
  motorSetDirectionPinsStateByDir(motorIsMoving);
  if (dir == 0){
    motorStop(1);
  }  
}

inline void motorStartChrDir(char dir){
  motorStart(chrToDir(dir));
}

void motorSetDirectionPinsStateByDir(int dir){
    digitalWrite(PIN_MOTOR_RIGHT, dir > 0 ? HIGH : LOW);
    digitalWrite(PIN_MOTOR_LEFT, dir < 0 ? HIGH : LOW);
};

inline void motorPause(byte doPause){
  motorSetDirectionPinsStateByDir((doPause == 0) ? motorIsMoving : 0);
}

void motorDrawMovementStatus(){
    motorPause(1);
    lcd.setCursor(3,0);
    lcd.print(
      (0 != motorIsMoving ) ? 
        motorMovingCharacters[motorMovingCharactersCursor] : 
        MOTOR_IS_STATIONARY_CHARACTER
    );
    motorPause(0);
}




// ================ WHEEL ====================

#define WHEEL_FILTERCOUNT 5
// N position: the number visible on the side,
// F position: the filter actually loaded
// F(N) = N % 5 + 1
#define COUNT_OF_CHANGES_BETWEEN_POSITIONS 123

#define WHEEL_FILTER_NAMES_ITEMSIZE 5
#define WHEEL_FILTER_NAMES_CHAR_ARRAY_SIZE ( WHEEL_FILTERCOUNT * WHEEL_FILTER_NAMES_ITEMSIZE )
#define WHEEL_FILTER_POSITION_ADDRESS 14
#define WHEEL_FILTER_NAMES_CHAR_ARRAY_ADDR (WHEEL_FILTER_POSITION_ADDRESS+2)
char wheelFilterNamesCharArray[WHEEL_FILTER_NAMES_CHAR_ARRAY_SIZE];
byte wheelNamesReadFromEEPROM = 0;

void wheelSaveNPosition(byte p){
    eep.write(WHEEL_FILTER_POSITION_ADDRESS, p);
};

byte wheelReadNPosition(){
    signed char p = eep.read(WHEEL_FILTER_POSITION_ADDRESS);
    if (p < 1){
      return 1;
    }
    if (p > WHEEL_FILTERCOUNT){
      return 1;
    }
    return p;
};


void wheelNamesToSerial(int destinationSerial){
  #define FILTERNAMES_HEAD "?filtrNs="
  #define FILTERNAMES_TAIL "?"
  if (0 == destinationSerial){
    Serial.print('#');
    Serial.print(FILTERNAMES_HEAD);
    for (uint16_t i=0; i<WHEEL_FILTER_NAMES_CHAR_ARRAY_SIZE; i++){
      Serial.print(wheelFilterNamesCharArray[i]);
    }
    Serial.print(FILTERNAMES_TAIL);
    Serial.println('/');
  }
  if (1 == destinationSerial){
    //this guy may be slower, so just enqueue some
    Serial.println("destination is 1");
    mySerial.print('#');
    mySerial.print(FILTERNAMES_HEAD);
    for (uint16_t i=0; i<WHEEL_FILTER_NAMES_CHAR_ARRAY_SIZE; i++){
      mySerial.print(wheelFilterNamesCharArray[i]);
    }
    mySerial.print(FILTERNAMES_TAIL);
    mySerial.println('/');
  }
}

void wheelNamesWrite(){  
  Serial.println("writing to eeprom");
  wheelNamesToSerial(0);
  for (uint16_t i=0; i<WHEEL_FILTER_NAMES_CHAR_ARRAY_SIZE; i++){
    eep.write(WHEEL_FILTER_NAMES_CHAR_ARRAY_ADDR + i, wheelFilterNamesCharArray[i]);
  }
  wheelNamesReadFromEEPROM = 0;
}

void wheelNamesRead(int destinationSerial){
  for (uint16_t i=0; i<WHEEL_FILTER_NAMES_CHAR_ARRAY_SIZE; i++){
    wheelFilterNamesCharArray[i] = eep.read(WHEEL_FILTER_NAMES_CHAR_ARRAY_ADDR + i);
  }
  wheelNamesToSerial(destinationSerial);
}

void wheelNamesCachedRead(){
    if (0 == wheelNamesReadFromEEPROM){
      wheelNamesReadFromEEPROM = 1;
      wheelNamesRead(-1);
    }
}



byte wheelNamesToggleStatus = 1;
void wheelNamesToggle(){
  if (1 == wheelNamesToggleStatus){
    wheelNamesToggleStatus = 0;
    wheelDisplayNPosition(0);
    lcd.setCursor(0,1);
    lcd.print("ABCD:move #d:set");
  }else{
    wheelNamesToggleStatus = 1;
    wheelDisplayNPosition(0);
    wheelNamesCachedRead();
    lcd.setCursor(4,0);    
    int i = 0;
    i+= 2;
    lcd.print(' ');
    lcd.print(' ');
    lcd.print(' ');
    lcd.print(NUMBER_1_FILTER_INDEX_CHARACTER);
    lcd.print(wheelFilterNamesCharArray[i++]);
    lcd.print(wheelFilterNamesCharArray[i++]);
    lcd.print(wheelFilterNamesCharArray[i++]);
    lcd.print(' '); 
    i+= 2;
    lcd.print(NUMBER_2_FILTER_INDEX_CHARACTER);
    lcd.print(wheelFilterNamesCharArray[i++]);
    lcd.print(wheelFilterNamesCharArray[i++]);
    lcd.print(wheelFilterNamesCharArray[i++]);
    lcd.print(' ');
    lcd.setCursor(0,1);
    lcd.print(' ');
    lcd.print(' ');
    i += 2;
    lcd.print(NUMBER_3_FILTER_INDEX_CHARACTER);
    lcd.print(wheelFilterNamesCharArray[i++]);
    lcd.print(wheelFilterNamesCharArray[i++]);
    lcd.print(wheelFilterNamesCharArray[i++]);
    lcd.print(' ');
    i+= 2;
    lcd.print(NUMBER_4_FILTER_INDEX_CHARACTER);
    lcd.print(wheelFilterNamesCharArray[i++]);
    lcd.print(wheelFilterNamesCharArray[i++]);
    lcd.print(wheelFilterNamesCharArray[i++]);
    lcd.print(' ');
    i+= 2;
    lcd.print(NUMBER_5_FILTER_INDEX_CHARACTER);
    lcd.print(wheelFilterNamesCharArray[i++]);
    lcd.print(wheelFilterNamesCharArray[i++]);
    lcd.print(wheelFilterNamesCharArray[i++]);
    lcd.print(' ');    
  }
};  




int wheelPosition = 1;
int wheelStopAfter = 0;

void wheelReportNPositionToSerialPorts(int isSetting){
  Serial.print("#?wheel");
  if (1 == isSetting){
    Serial.print("Set");
  };
  Serial.print("Position=");
  Serial.print(wheelPosition);
  Serial.print("?/");
  mySerial.print("#?wheelPosition=");
  mySerial.print(wheelPosition);
  mySerial.print("?/");
};

void wheelDisplayNPosition(int isSetting){
  lcd.setCursor(0,0);
  lcd.print("N=");
  lcd.print(wheelPosition);
  lcd.print("       d:goto");
  motorDrawMovementStatus();
  wheelReportNPositionToSerialPorts(isSetting);
}

void wheelConsiderWeAreAtNPosition(int n){
  wheelPosition = n;
  wheelSaveNPosition(n);
  wheelDisplayNPosition(1);
};


void wheelMove(int dir, int count){
  optoObserver.changes = 0;
  wheelStopAfter = abs(count) * COUNT_OF_CHANGES_BETWEEN_POSITIONS - 1;
  if (wheelStopAfter < 0){
    wheelStopAfter = 0;
  }
  motorStart(dir);
  wheelPosition = wheelPosition + count*dir;
  while (wheelPosition > WHEEL_FILTERCOUNT){
    wheelPosition -= WHEEL_FILTERCOUNT;
  }
  while (wheelPosition < 1){
    wheelPosition += WHEEL_FILTERCOUNT;
  }
  wheelDisplayNPosition(0);
}

void wheelMoveFullCircleChrDir(char dir){
  wheelMove(chrToDir(dir), WHEEL_FILTERCOUNT);
};


inline void wheelMoveChrDir(char dir, int count){
  wheelMove(chrToDir(dir), count);
};


void wheelMarkInputStateChange(){
  if (wheelStopAfter > 0){
    wheelStopAfter--;
    if (wheelStopAfter == 0){
      motorStop(1);
    }
  }
}

void wheelGotoNPosition(int n){
    int zeroBasedCurrentPosition = wheelPosition - 1;
    int zeroBasedDestination = n - 1;

    int delta = zeroBasedDestination - zeroBasedCurrentPosition;
    if (abs(delta) > WHEEL_FILTERCOUNT / 2){
      int s = delta < 0 ? -1 : 1;
      delta = (WHEEL_FILTERCOUNT - abs(delta))*(s*-1);      
    }
    /*
    lcd.setCursor(0,1);
    lcd.print("delta=");
    lcd.print(delta);
    lcd.print(" ");
    */
    wheelSaveNPosition(zeroBasedDestination + 1);
    wheelMove(-delta, -delta);
    wheelPosition = zeroBasedDestination + 1;
    wheelDisplayNPosition(1);    
}

int wheelNextKeyIsNSetter = 0;

void wheelMoveALittle(char dir, int steps){
    wheelStopAfter = steps;
    motorStartChrDir(dir);
}

void wheelNamesSetIndexZ(int zi, char * subname, int doWrite){
  if (zi < 0){
    return ;//invalid index
  }
  if (zi >= WHEEL_FILTERCOUNT){
    return ; // invalid index
  }
  #ifdef FILTERWHEEL_PROJECT_DEBUG_MODE
  Serial.print("Setting name ");
  Serial.print(zi);
  Serial.print(" to ");
  Serial.println(subname);
  #endif
  int i = zi*WHEEL_FILTER_NAMES_ITEMSIZE;
  wheelFilterNamesCharArray[i++] = zi+48+1;
  wheelFilterNamesCharArray[i++] = '=';  
  byte j=0;
  for (byte k=0; k<WHEEL_FILTER_NAMES_ITEMSIZE-2;k++){
    wheelFilterNamesCharArray[i+k] = (j<strlen(subname)) ? subname[j++] : ' ';
  }
  if (1 == doWrite){
    wheelNamesWrite();
  };
}




int command_process(const char * filterWheelSerialReadBuffer, int destinationSerial){  
  if (strncmp(filterWheelSerialReadBuffer, "filter", 6) == 0){
     wheelMoveChrDir(filterWheelSerialReadBuffer[6], 1);
     return 1;
  }
  if (strncmp(filterWheelSerialReadBuffer, "jump", 4) == 0){
    wheelMoveALittle(filterWheelSerialReadBuffer[4], 10);
    return 1;
  }
  if (strncmp(filterWheelSerialReadBuffer, "move", 4) == 0){
    wheelMoveALittle(filterWheelSerialReadBuffer[4], 4);
    return 1;
  }
  if (strncmp(filterWheelSerialReadBuffer, "step", 4) == 0){
    wheelMoveALittle(filterWheelSerialReadBuffer[4], 1);
    return 1;
  }
  
  if (strncmp(filterWheelSerialReadBuffer, "atn,", 4) == 0){
    wheelConsiderWeAreAtNPosition(filterWheelSerialReadBuffer[4] - 48);
    wheelStopAfter = 0;
    motorStop(1);
    return 1;
  }
  if (strncmp(filterWheelSerialReadBuffer, "goton,", 6) == 0){    
    wheelGotoNPosition(filterWheelSerialReadBuffer[6] - 48);
    return 1;
  }
  if (strncmp(filterWheelSerialReadBuffer, "goton=", 6) == 0){    
    wheelGotoNPosition(filterWheelSerialReadBuffer[6] - 48);
    return 1;
  }
  if (strncmp(filterWheelSerialReadBuffer, "wheren", 6) == 0){
    wheelReportNPositionToSerialPorts(0);
    return 1;
  }
  if (strncmp(filterWheelSerialReadBuffer, "eepnames?", 9) == 0){
    wheelNamesRead(destinationSerial);    
    return 1;
  }
  if (strncmp(filterWheelSerialReadBuffer, "name",4)==0){
    #ifdef DEBUG_MODE
    Serial.println(filterWheelSerialReadBuffer);
    #endif
    if (filterWheelSerialReadBuffer[4] == 's'){
      // names=a,b,c,d,e,f,g,h
      char subname[16];
      subname[0] = 0;
      int subnameIndex = 0;
      for (int i=6; i<64; i++){
        if ((filterWheelSerialReadBuffer[i] == ',') || (filterWheelSerialReadBuffer[i] == 0)){          
          wheelNamesSetIndexZ(subnameIndex, subname, 0);
          if (filterWheelSerialReadBuffer[i] == 0){
            wheelNamesWrite();
            return ;
          }
          subname[0] = 0;
          subnameIndex++;
        }else{
          int l = strlen(subname);
          if (l >= WHEEL_FILTER_NAMES_ITEMSIZE-1){
            l = WHEEL_FILTER_NAMES_ITEMSIZE-2;
          }
          subname[l] = filterWheelSerialReadBuffer[i];
          subname[l+1] = 0;
        }
      }
      //wheelNamesWrite();
    }else{
      // nameN=xyzqw
      strcat(filterWheelSerialReadBuffer, "   ");
      int n = filterWheelSerialReadBuffer[4] - 48;
      wheelNamesSetIndexZ(n-1, filterWheelSerialReadBuffer+6, 1);      
    }
    return 1;
  }  
  return 0;
}





void setup(){
  lcd.init();
  lcd.backlight();
  lcd.setCursor(0,0);
  lcd.print(FILTERWHEEL_WELCOME_SCREEN_FIRST_LINE);
  lcd.setCursor(0,1);
  lcd.print(FILTERWHEEL_WELCOME_SCREEN_SECOND_LINE);
  optoInit();
  
  motorInit();

  serialInit();

  #ifndef FILTERWHEEL_PROJECT_DEBUG_MODE
  wheelNamesCachedRead();
  wheelPosition = wheelReadNPosition();
  delay(1000);
  #endif
  keyboardInit();
  lcd.setCursor(0,0);
  lcd.print("Set the cur. N  ");
  wheelNamesToggle();
  #ifdef FILTERWHEEL_PROJECT_DEBUG_MODE
  Serial.println("debug mode");
  wheelNamesRead(0);
  #endif
}




void loop() {
  if (OPTO_CHANGE_OBSERVED == optoObserverProcess()){
    wheelMarkInputStateChange();
  }  
  serialProcess();
  keyboardProcess();
  motorProcess();

  currentKey = keyboardGetCurrentKey();
  if (currentKey != 0){
    if (currentKey == '*'){
      wheelNamesToggle();
    }    
    if (currentKey == '0'){
      resetFunc();
    }
    if (0 != motorIsMoving){
      // do not execute keyboard commands
    }else{
      if ((currentKey >= 49) && (currentKey <= 57)){
        //if (currentKey == '8'){ wheelMoveFullCircleChrDir('R'); }
        if ((currentKey - 48) > WHEEL_FILTERCOUNT){
          //invalid filter N position, zero already handled
          return ;
        }
        if (1 == wheelNamesToggleStatus){
          wheelNamesToggle();
        }
        if (1 == wheelNextKeyIsNSetter){
          wheelConsiderWeAreAtNPosition(currentKey - 48);
          wheelStopAfter = 0;
          motorStop(1);
        }else{
          wheelGotoNPosition(currentKey - 48);          
        }
      }
      if (currentKey == '#'){
        if (1 == wheelNextKeyIsNSetter){
          wheelNextKeyIsNSetter = 0;
          wheelDisplayNPosition(0);
        }else{
          wheelNextKeyIsNSetter = 1;
          lcd.setCursor(2,0);
          lcd.print('?');
        }
      }else{
        wheelNextKeyIsNSetter = 0;
      }
      if (currentKey == 'A'){
        wheelMoveALittle('L', 4);
      }
      if (currentKey == 'D'){
        wheelMoveALittle('R', 4);
      }
      if (currentKey == 'B'){
        wheelMoveALittle('L', 2);
      }
      if (currentKey == 'C'){
          wheelMoveALittle('R', 2);
      }
    }
  }

}
facebooktwittergoogle_plusredditpinterestlinkedinmail