├── Arduino_LENS_SERIAL_001.ino ├── ControlCanonLens.cpp ├── ControlCanonLens.h └── README.md /Arduino_LENS_SERIAL_001.ino: -------------------------------------------------------------------------------- 1 | #include "ControlCanonLens.h" 2 | 3 | #define THIS_INTERFACE_ID 12 4 | 5 | /*#include // *** I2C Mode 6 | #define OLED_Address 0x3c 7 | #define OLED_Command_Mode 0x80 8 | #define OLED_Data_Mode 0x40 9 | */ 10 | ControlLoopLENS controller; 11 | 12 | //IntervalTimer innerTimer; 13 | 14 | byte outgoingBuffer[8]; 15 | byte incomingBuffer[8]; 16 | 17 | 18 | // ------------------------------------------- 19 | // 20 | // STATES 21 | // 22 | // ------------------------------------------- 23 | 24 | //#define DEBUG_ENABLED 25 | 26 | #define STATE_MOTOR_OFF 0 27 | #define STATE_MOTOR_ON_IDLE 1 28 | #define STATE_MOTOR_ON_PLAYING_FRAME 2 29 | #define STATE_MOTOR_OFF_OUT_OF_BOUNDS 3 30 | #define STATE_MOTOR_OFF_RAN_OUT_OF_FRAMES 4 31 | #define STATE_DOING_STICTION_CALIBRATION 5 32 | #define BUFFER_STATE_NEED_NEXT_FRAME 1 33 | #define BUFFER_STATE_OK 0 34 | 35 | uint8_t state; 36 | uint8_t buffer_state; 37 | long lastMillis = 0; 38 | 39 | void stepperIntHandler() { 40 | controller.update(); 41 | } 42 | 43 | 44 | 45 | 46 | #pragma pack(push,1) 47 | 48 | union __attribute__((packed)) { 49 | int16_t output; 50 | uint8_t input[2]; 51 | } 52 | buffer; 53 | 54 | #pragma pack(pop) 55 | 56 | 57 | int16_t nextPosition; 58 | 59 | #ifdef DEBUG_ENABLED 60 | 61 | #include 62 | Adafruit_CharacterOLED lcd(9, 8, 10, 7, 6, 5, 4); 63 | 64 | #endif 65 | 66 | uint8_t addressOffset = 0; 67 | 68 | void setup() { 69 | 70 | // sort out address 71 | pinMode(2,INPUT); 72 | pinMode(3,INPUT); 73 | digitalWrite(2,HIGH); 74 | digitalWrite(3,HIGH); 75 | 76 | 77 | if (digitalRead(3)) { addressOffset += 1; } 78 | if (digitalRead(2)) { addressOffset += 2; } 79 | 80 | 81 | 82 | // Wire.begin(); 83 | // OLED_start(); 84 | #ifdef DEBUG_ENABLED 85 | 86 | lcd.begin(16, 2); 87 | lcd.print("Starting up"); 88 | #endif 89 | 90 | 91 | // put your setup code here, to run once: 92 | Serial.begin(115200); 93 | // Serial2.begin(115200); 94 | pinMode(13, OUTPUT); 95 | // pinMode(23, INPUT); 96 | // digitalWrite(23, HIGH); 97 | // delay(500); 98 | while (!Serial) { 99 | ;// wait 100 | } 101 | 102 | while (Serial.available() > 0) { 103 | Serial.read(); 104 | } 105 | // 106 | 107 | 108 | controller.init(); 109 | // innerTimer.begin(stepperIntHandler, 16); 110 | // innerTimer.priority(20); 111 | 112 | state = STATE_MOTOR_ON_IDLE; 113 | lastMillis = millis(); 114 | 115 | #ifdef DEBUG_ENABLED 116 | 117 | lcd.clear(); 118 | #endif 119 | 120 | } 121 | 122 | 123 | 124 | uint8_t charsExpected = 0; 125 | bool needNextData = true; 126 | bool flasher = false; 127 | 128 | void loop() { 129 | 130 | if ((millis() - lastMillis) > 40) { 131 | lastMillis += 40; 132 | // controller.update(); 133 | 134 | controller.setSetpoint(getNextDelta()); 135 | controller.update(); 136 | 137 | // OLED_numprint(controller._currentFocus, 0, 1); 138 | // updateScreen(); 139 | } 140 | checkForInput(); 141 | //controller.update(); 142 | 143 | } 144 | 145 | 146 | 147 | 148 | int16_t getNextDelta() { 149 | if (state == STATE_MOTOR_ON_PLAYING_FRAME) { 150 | if (buffer_state == BUFFER_STATE_OK) { 151 | buffer_state = BUFFER_STATE_NEED_NEXT_FRAME; 152 | doSendUpdate(); 153 | } else { 154 | //state = STATE_MOTOR_OFF_RAN_OUT_OF_FRAMES; 155 | doSendUpdate(); 156 | //state = STATE_MOTOR_ON_IDLE; 157 | 158 | // doSendUpdate(); 159 | 160 | } 161 | } 162 | return nextPosition; 163 | 164 | } 165 | 166 | int16_t charsReceived = 0; 167 | long lastInputMillis = 0; 168 | 169 | 170 | 171 | 172 | void checkForInput() { 173 | 174 | 175 | if (Serial.available() > 0) { 176 | 177 | 178 | if ((millis() - lastInputMillis) > 1000) { 179 | charsReceived = 0; 180 | 181 | } 182 | lastInputMillis = millis(); 183 | 184 | incomingBuffer[charsReceived] = Serial.read(); 185 | charsReceived++; 186 | if (charsReceived >= 8) { 187 | charsReceived = 0; 188 | handleInput(); 189 | } 190 | 191 | } 192 | } 193 | 194 | 195 | 196 | void handleInput() { 197 | 198 | switch (incomingBuffer[0]) { 199 | case 104: 200 | // 'h' 201 | // digitalWrite(6,HIGH); 202 | controller.lensHome(); 203 | // digitalWrite(6,LOW); 204 | break; 205 | case 105: 206 | // identify! 207 | sendIdentificationPacket(); 208 | break; 209 | case 102: 210 | // reset! 211 | controller.lensMoveFurthest(); 212 | break; 213 | case 110: 214 | // reset! 215 | controller.lensMoveNearest(); 216 | break; 217 | case 114: 218 | // reset! 219 | controller.init(); 220 | break; 221 | case 0: 222 | // manual control 223 | //controller.disableMotor(); 224 | state = STATE_MOTOR_OFF; 225 | break; 226 | case 1: 227 | //controller.enableMotor(); 228 | state = STATE_MOTOR_ON_IDLE; 229 | break; 230 | case 2: 231 | receiveFrame(); 232 | break; 233 | case 16: 234 | doSendUpdate(); 235 | break; 236 | } 237 | 238 | } 239 | 240 | 241 | 242 | 243 | 244 | int32_t getLongFromIncomingBufferAtPosition(uint8_t bufferPosition) { 245 | int32_t theVal = ((int32_t) incomingBuffer[bufferPosition] & 0xFF); 246 | theVal |= (((int32_t) incomingBuffer[bufferPosition + 1] & 0xFF)) << 8; 247 | theVal |= (((int32_t) incomingBuffer[bufferPosition + 2] & 0xFF)) << 16; 248 | theVal |= (((int32_t) incomingBuffer[bufferPosition + 3] & 0xFF)) << 24; 249 | return theVal; 250 | } 251 | 252 | void receiveFrame() { 253 | nextPosition = (int16_t) getLongFromIncomingBufferAtPosition(1); 254 | buffer_state = BUFFER_STATE_OK; 255 | // if it's the first frame, set it, reset timers 256 | if (state == STATE_MOTOR_ON_IDLE) { 257 | state = STATE_MOTOR_ON_PLAYING_FRAME; 258 | controller.setSetpoint(nextPosition); 259 | lastMillis = millis(); 260 | 261 | getNextDelta(); 262 | // controller.resetAndStartAnim(); 263 | } else { 264 | //buffer_state = BUFFER_STATE_NEED_FRAME; 265 | } 266 | //buffer_state = BUFFER_STATE_OK; 267 | 268 | #ifdef DEBUG_ENABLED 269 | 270 | //lcd.clear(); 271 | lcd.setCursor(8,0); 272 | lcd.print(nextPosition); 273 | lcd.print (" "); 274 | 275 | #endif 276 | } 277 | 278 | 279 | 280 | 281 | void sendIdentificationPacket() { 282 | outgoingBuffer[0] = 0; 283 | outgoingBuffer[1] = 1; 284 | outgoingBuffer[2] = 1; 285 | outgoingBuffer[3] = THIS_INTERFACE_ID + addressOffset; 286 | outgoingBuffer[4] = 'H'; 287 | sendBuffer(); 288 | } 289 | 290 | void doSendUpdate() { 291 | outgoingBuffer[0] = 1; // live data 292 | outgoingBuffer[1] = 1; 293 | outgoingBuffer[2] = state; 294 | outgoingBuffer[3] = buffer_state; 295 | putIntInOutgoingBufferAtPosition(controller._requestedSetpoint, 4); 296 | putIntInOutgoingBufferAtPosition(controller._lensOffset, 6); 297 | // putLongInOutgoingBufferAtPosition(controller.homeSensorPosition, 44); 298 | #ifdef DEBUG_ENABLED 299 | 300 | //lcd.clear(); 301 | lcd.setCursor(0,0); 302 | lcd.print(controller._requestedSetpoint); 303 | lcd.print (" "); 304 | lcd.setCursor(0,1); 305 | lcd.print(controller._lensOffset); 306 | lcd.print (" "); 307 | 308 | #endif 309 | 310 | sendBuffer(); 311 | } 312 | 313 | 314 | 315 | void sendBuffer() { 316 | // for (int i = 0; i < 8; i++) { 317 | //Serial.flush(); 318 | //delay(10); 319 | Serial.write(outgoingBuffer,8); 320 | Serial.flush(); 321 | // } 322 | } 323 | 324 | void putIntInOutgoingBufferAtPosition(int16_t theValue, uint8_t bufferPosition) { 325 | outgoingBuffer[bufferPosition] = theValue & 0xFF; 326 | outgoingBuffer[bufferPosition + 1] = (theValue >> 8) & 0xFF; 327 | } 328 | 329 | 330 | void putLongInOutgoingBufferAtPosition(long theValue, uint8_t bufferPosition) { 331 | outgoingBuffer[bufferPosition] = theValue & 0xFF; 332 | outgoingBuffer[bufferPosition + 1] = (theValue >> 8) & 0xFF; 333 | outgoingBuffer[bufferPosition + 2] = (theValue >> 16) & 0xFF; 334 | outgoingBuffer[bufferPosition + 3] = (theValue >> 24) & 0xFF; 335 | } 336 | 337 | -------------------------------------------------------------------------------- /ControlCanonLens.cpp: -------------------------------------------------------------------------------- 1 | 2 | //#ifndef TEENSYDUINO 3 | 4 | #include "ControlCanonLens.h" 5 | #include 6 | //#ifdef CONTROL_TYPE_LENS 7 | 8 | 9 | //bidirectional clock PF4 - A3 on micro 10 | #define CLK_OUTPORT PORTC 11 | #define CLK_INPORT PINC 12 | #define CLK_DIR DDRC 13 | #define CLK_BIT 0 14 | #define clkOut() CLK_DIR |= (1 << CLK_BIT) 15 | #define clkIn() CLK_DIR &= ~(1 << CLK_BIT) 16 | #define clockIsHigh() (CLK_INPORT & (1 << CLK_BIT)) 17 | #define clkHigh() CLK_OUTPORT |= (1 << CLK_BIT) 18 | #define clkLow() CLK_OUTPORT &= ~(1 << CLK_BIT) 19 | 20 | //output to lens (Arduino Digital I/O 12 = PB6) 21 | #define DCO_PORT PORTC 22 | #define DCO_DIR DDRC 23 | #define DCO_BIT 1 24 | 25 | //input from lens (Arduino Digital I/O 9 = PH6) 26 | #define DCI_OUTPORT PORTC 27 | #define DCI_INPORT PINC 28 | #define DCI_DIR DDRC 29 | #define DCI_BIT 2 30 | 31 | 32 | 33 | 34 | #define BIT_LEN 10 35 | 36 | //iterations to hold after receiving end of lens busy, before next byte 37 | #define POST_BUSY_WAIT 40 38 | 39 | //how long to wait for lens busy (to set or clear) before giving up entirely. 40 | // NB: The lens will take forever to respond to motor commands if there is no motor voltage! 41 | #define BUSY_TIMEOUT 3000 42 | 43 | // Storage buffer 44 | #define STORAGE_SIZE 256 45 | uint8_t storage[STORAGE_SIZE]; 46 | int storagePos = 0; 47 | 48 | extern int32_t getNextDelta(); 49 | 50 | ControlLoopLENS::ControlLoopLENS() { 51 | } 52 | 53 | void ControlLoopLENS::init() { 54 | clkOut(); // clock as output to start 55 | DCO_DIR |= (1 << DCO_BIT); // DCO as output 56 | DCI_DIR &= ~(1 << DCI_BIT); // DCI as input 57 | DCI_OUTPORT |= (1 << DCI_BIT); // with pull-up enabled 58 | // DBG_DIR |= (1 << DBG_BIT); // debug as output 59 | 60 | clkHigh(); 61 | 62 | //targetVelocity = 0; 63 | 64 | // motor = new Motor(pmot_G, pmot_E, pmot_L, pmot_R); 65 | //_loopCount = 0; 66 | lensInit(); 67 | // lensHome(); 68 | // lensApertureChange(0x79); 69 | // delay(200); 70 | // lensApertureChange(0x80); 71 | } 72 | 73 | 74 | int16_t ControlLoopLENS::getSetpoint() { 75 | return _requestedSetpoint; 76 | } 77 | 78 | 79 | // --------------------- DIFFERENT LENS stuff! 80 | 81 | void ControlLoopLENS::setSetpoint(int16_t newVal) { 82 | _requestedSetpoint = newVal; 83 | } 84 | 85 | bool ControlLoopLENS::lensMoving() { 86 | waitForCameraNotBusy(); 87 | storagePos = 0; 88 | doByte (0x90); 89 | doByte (0xB9); 90 | doByte (0x00); 91 | if ((storage[5] == 4) || (storage[5] == 36)) { 92 | digitalWrite(13,HIGH); 93 | return true; 94 | } else { 95 | digitalWrite(13,LOW); 96 | return false; 97 | } 98 | 99 | } 100 | 101 | void ControlLoopLENS::waitForLensToStop() { 102 | int timeout = 1000; 103 | while (lensMoving()) { 104 | timeout --; 105 | if (timeout < 1) break; 106 | } 107 | } 108 | 109 | // ----------------------------------------- 110 | 111 | 112 | void ControlLoopLENS::setZero() { 113 | _lensOffset = lensGetPosition(); 114 | _requestedSetpoint = 0; 115 | _currentPosition = 0; 116 | _currentFocus = _lensOffset; 117 | _currentIris = 0; 118 | // _currentVelocity = 0; 119 | } 120 | 121 | 122 | void ControlLoopLENS::nudge(int32_t distance) { 123 | _requestedSetpoint += distance; 124 | moveLens((int16_t) distance); 125 | } 126 | 127 | void ControlLoopLENS::moveLens(int16_t delta) { 128 | 129 | // digitalWrite(3,(abs(delta) > 10)); 130 | // digitalWrite(4,(abs(delta) > 50)); 131 | int8_t lowB = 0xFF & delta; 132 | delta >>= 8; 133 | storagePos = 0; 134 | doByte(0x44); 135 | 136 | doByte(0xFF & delta); 137 | doByte(lowB); 138 | 139 | } 140 | 141 | 142 | 143 | void ControlLoopLENS::update(void) { 144 | 145 | if (!lensMoving()) { 146 | _currentPosition = lensGetPosition() - _lensOffset; 147 | if (_currentPosition != _requestedSetpoint) { 148 | moveLens(_requestedSetpoint - _currentPosition); 149 | } 150 | 151 | } 152 | // int32_t theDelta = _currentPosition - _requestedSetpoint; 153 | // _currentPosition += theDelta; 154 | 155 | // moveLens(theDelta); // - _currentFocus); 156 | 157 | 158 | } 159 | 160 | 161 | uint8_t ControlLoopLENS::doByte(uint8_t byteToSend) { 162 | if (storagePos >= STORAGE_SIZE) { 163 | storagePos = 0; 164 | } 165 | clkHigh(); //clock should be high already 166 | clkOut(); 167 | 168 | noInterrupts(); //timing is critical and Arduino keeps doing things 169 | 170 | uint8_t byteIn; 171 | uint8_t byteOut = byteToSend; 172 | for (int i = 0; i < 8; i++) { //for each bit 173 | 174 | //set the output bit 175 | DCO_PORT = (DCO_PORT & ~(1 << DCO_BIT)) | ((byteOut & 0x80) >> (7 - DCO_BIT)); 176 | byteOut = (byteOut << 1); //move to next bit 177 | 178 | clkLow(); //drop the clock, the lens sets our DCI after this edge. 179 | // dbgLow(); 180 | 181 | for (int j = 0; j < BIT_LEN; j++); //bit delay on low clock 182 | __asm__("nop\n\t"); 183 | 184 | clkHigh(); //raise the clock. The lens reads our DCO after this edge. 185 | uint8_t dciVal = DCI_INPORT; 186 | byteIn = (byteIn << 1) | ((dciVal >> DCI_BIT) & 0x01); //store the DCI bit in byteIn and shift 187 | // dbgHigh(); 188 | 189 | for (int j = 0; j < BIT_LEN; j++); //bit delay on high clock 190 | __asm__("nop\n\t"); 191 | 192 | } 193 | 194 | 195 | clkHigh(); //enable pull-up for clock 196 | clkIn(); //and release the clock line output 197 | 198 | // dbgLow(); //debug signature for end of byte 199 | // dbgHigh(); 200 | // dbgLow(); 201 | 202 | //Delay a bit to give the lens time to pull the clock low to signify it is busy 203 | // (we don't assume it always does, although it does appear to) 204 | for (int j = 0; j < BIT_LEN; j++); 205 | __asm__("nop\n\t"); 206 | // dbgHigh(); //dbg signature for 'busy received' 207 | // 208 | //wait for lens busy to end. This can take a long time, or forever 209 | int timeout = 0; 210 | while (!clockIsHigh()) { 211 | timeout++; 212 | if (timeout > BUSY_TIMEOUT) 213 | goto busyTimeout; 214 | }; 215 | //dbgLow(); //debug signature: busy complete 216 | // dbgHigh(); 217 | // dbgLow(); 218 | 219 | // wait a little bit longer before proceeding with the next byte 220 | for (int j = 0; j < POST_BUSY_WAIT; j++); 221 | __asm__("nop\n\t"); 222 | 223 | // dbgHigh(); 224 | 225 | clkOut(); 226 | interrupts(); //end of critical timing 227 | 228 | //store and return 229 | storage[storagePos++] = byteToSend; 230 | storage[storagePos++] = byteIn; 231 | delay(1); 232 | // digitalWrite(13,LOW); 233 | return byteIn; 234 | 235 | busyTimeout: 236 | /* dbgLow(); //debug error signature 237 | dbgHigh(); 238 | dbgLow(); 239 | dbgHigh(); 240 | dbgLow(); 241 | dbgHigh(); 242 | dbgLow(); */ 243 | storage[storagePos++] = 0xDE; //mark command failure (maybe) 244 | storage[storagePos++] = 0xAD; 245 | // digitalWrite(13,HIGH); 246 | return 0xFF; 247 | } 248 | 249 | 250 | void ControlLoopLENS::waitForCameraNotBusy() { 251 | //Serial.print("Busy? "); 252 | 253 | 254 | bool cameraBusy = true; 255 | //digitalWrite(13,HIGH); 256 | while (cameraBusy) { 257 | storagePos = 0; 258 | doByte(0x0A); 259 | doByte(0x00); 260 | if (storage[3] == 0xAA) { 261 | cameraBusy = false; 262 | } else { 263 | // Serial.print("."); 264 | delay(5); 265 | } 266 | 267 | } 268 | //digitalWrite(13,LOW); 269 | //Serial.println("OK"); 270 | } 271 | 272 | bool ControlLoopLENS::lensNotBusy() { 273 | storagePos = 0; 274 | doByte(0x0A); 275 | doByte(0x00); 276 | return (storage[3] == 0xAA); 277 | } 278 | 279 | 280 | void ControlLoopLENS::lensMoveNearest() { 281 | doByte(0x06); 282 | //waitForCameraNotBusy(); 283 | } 284 | 285 | void ControlLoopLENS::lensMoveFurthest() { 286 | doByte(0x05); 287 | //waitForCameraNotBusy(); 288 | } 289 | 290 | int16_t ControlLoopLENS::lensGetPosition() { 291 | waitForCameraNotBusy(); 292 | storagePos = 0; 293 | doByte(0xC0); 294 | doByte(0x00); 295 | doByte(0x00); 296 | int16_t theFoc = ((int16_t) storage[3] << 8) | storage[5]; 297 | //theFoc -= _lensOffset; 298 | if (abs(theFoc - _lensOffset) > 10) { 299 | //digitalWrite(5,HIGH); 300 | } else { 301 | //digitalWrite(5,LOW); 302 | 303 | } 304 | return (theFoc); 305 | } 306 | 307 | 308 | 309 | 310 | 311 | 312 | void ControlLoopLENS::lensHome() { 313 | storagePos = 0; 314 | doByte(0x05); 315 | waitForLensToStop(); 316 | //delay(1200); 317 | int16_t lensMax = lensGetPosition(); 318 | 319 | doByte(0x06); 320 | 321 | //delay(1200); 322 | waitForLensToStop(); 323 | 324 | _lensOffset = lensGetPosition(); 325 | _lensScale = lensMax - _lensOffset; 326 | 327 | 328 | // doByte(0x05); 329 | _currentPosition = 0; 330 | _requestedSetpoint = 0; 331 | 332 | } 333 | 334 | 335 | void ControlLoopLENS::lensApertureOpen() { 336 | storagePos = 0; 337 | doByte (0x13); 338 | doByte (0x80); 339 | waitForCameraNotBusy(); 340 | _currentIris = 0; 341 | } 342 | 343 | void ControlLoopLENS::lensApertureChange(int8_t amount) { 344 | waitForCameraNotBusy(); 345 | storagePos = 0; 346 | // doByte (0x07); 347 | doByte (0x13); 348 | doByte (amount); 349 | _currentIris += amount; 350 | 351 | } 352 | 353 | 354 | void ControlLoopLENS::lensInit() { 355 | waitForCameraNotBusy(); 356 | storagePos = 0; 357 | delayMicroseconds(100); 358 | 359 | doByte(0x90); // ??? 360 | doByte(0x8E); 361 | doByte(0x00); 362 | 363 | delayMicroseconds(100); 364 | 365 | doByte(0xB0); // request aperture data 366 | doByte(0x00); 367 | doByte(0x00); 368 | doByte(0x00); 369 | storagePos = 0; 370 | delayMicroseconds(100); 371 | 372 | doByte(0x80); // request lens data 373 | doByte(0x00); //? lens code 374 | doByte(0x00); // lens code 375 | doByte(0x00); 376 | doByte(0x00); // focal len in mm 377 | doByte(0x00); 378 | doByte(0x00); // focal len in mm (maybe they are min/max?) 379 | doByte(0x00); 380 | storagePos = 0; 381 | 382 | //dumpStorage(); 383 | lensHome(); 384 | nextDelta = 0; 385 | 386 | } 387 | 388 | 389 | //#endif 390 | -------------------------------------------------------------------------------- /ControlCanonLens.h: -------------------------------------------------------------------------------- 1 | #ifndef ___ControlLoopLENS___ 2 | #define ___ControlLoopLENS___ 3 | 4 | //#include "Config.h" 5 | 6 | 7 | #include 8 | #include 9 | //#include "Motor.h" 10 | //#include 11 | #define MANUAL 0 12 | #define AUTOMATIC 1 13 | 14 | 15 | class ControlLoopLENS { 16 | 17 | int8_t _currentIris; 18 | 19 | int32_t nextDelta; 20 | int16_t _lensScale; 21 | 22 | void waitForCameraNotBusy(); 23 | uint8_t doByte(uint8_t byteToSend); 24 | bool lensNotBusy(); 25 | 26 | public: 27 | // lens moves 28 | int16_t _lensOffset; 29 | bool lensMoving(); 30 | void waitForLensToStop(); 31 | int16_t _currentPosition; 32 | int16_t _requestedSetpoint; 33 | 34 | int16_t _currentFocus; 35 | void lensMoveNearest(); 36 | void lensMoveFurthest(); 37 | int16_t lensReadPosition(); 38 | int16_t lensGetPosition(); 39 | void lensHome(); 40 | 41 | 42 | // iris moves 43 | void lensApertureOpen(); 44 | void lensApertureChange(int8_t amount); 45 | 46 | 47 | void lensInit(); 48 | void moveLens(int16_t delta); 49 | 50 | 51 | 52 | 53 | //public: 54 | 55 | ControlLoopLENS(); 56 | 57 | void init(); 58 | 59 | void update(void); 60 | 61 | void setSetpoint(int16_t); 62 | int16_t getSetpoint(); 63 | 64 | void nudge(int32_t distance); 65 | void setZero(); 66 | 67 | 68 | 69 | 70 | 71 | 72 | }; 73 | 74 | //ControlLoopSTEPPER controller; 75 | 76 | 77 | #endif 78 | 79 | 80 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # h's homemade Motion Control: firmware for Arduino plus Canon EF Lens 2 | Introduction 3 | 4 | This code is designed to be uploaded to an Arduino, which is in turn connected to a Canon EF-series autofocus lens. The microcontroller can then be connected to a Mac via USB, and the Motion Control 5 | Server app run, which will allow full (and kinematically safe) manual control of the lens focus via on-screen sliders, and the playback of 6 | sequences created in Blender. 7 | 8 | This firmware uses undocumented Canon protocols to control the lens, and has variable levels of success depending on the actual lens 9 | used. For best results, use a Canon ring-type Ultrasonic lens; these are the only ones that seem to offer good repeatability. I get excellent results 10 | with a Canon EF-S 17-55 IS USM lens. (Note that not all lenses marked "Ultrasonic" actually offer the true benefits of a ring-type motor) 11 | 12 | This code does nothing on its own, and needs the Mac app in order to do anything useful. 13 | 14 | To see it working, watch this: https://www.youtube.com/watch?v=rWuKmWKicro 15 | For the server code, see this project: https://github.com/howiemnet/MotionControl 16 | 17 | Warnings 18 | 19 | This code contains more bugs than working lines of code. I will not be offering support or help in understanding 20 | what the hell is going on beyond what I can get written up for my blog. It's just too big and complex a project: it relies 21 | on not just this app working correctly, but the right hardware wiring up, and the appropriate firmware running on 22 | the various microcontrollers involved. 23 | 24 | That said, it's my intention to try and tidy this up and document it well enough that the project will be reproducable, in the 25 | hope that others may be able to hack together motion control systems without having to re-invent these wheels. 26 | 27 | A lot more documentation will come: this is just the first commit to get it all up there - feel free to poke around and laugh 28 | at my appalling coding style. Just keep it to yourself, y'all 29 | 30 | :) 31 | 32 | h 17/7/2016 33 | --------------------------------------------------------------------------------