/* Kiteside KAP Controls, based on AtMega168 & Xbee Licensed under The MIT License Copyright (c) 2007-2008 Ben Peoples - ben@benpeoples.com Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Assumes Xbee is already configured via other interface. Serial commands: L = Rotate "left" (Servo = 1000) R = Rotate "right" (Servo = 2000) X = Stop rotating S = Take picture T + Byte = Set tilt to byte, 0 = Horizontal, 255 = Vertical M = Set mode to manual (all functions manual control) I = Set mode to Intervalometer (all functions manual, shutter triggers every INTERVAL or INTERVAL*6 (see switches) -- responds to all commands (including "S") A = Set mode to Autokap (Manual tilt, shutter & 350ms Pan every INTERVAL) N = "New Mode" Undefined as yet. Onboard Switches: SW1 = When Off: Normal Mode When On: Autokap, listens to LRXST commands, but ignores MIAN SW2 = When Off: INTERVAL (~5 seconds) "Digital" When On: INTERVAL*6 (~30 seconds) "Film" -- also disables timeout Autokap Timeout: The TX should send "X" or mode commands periodically to the RX -- if no signal is recieved within 5 seconds, it will go into autokap mode assuming it is out of range of the receiver. HOWEVER if SW2 is on, the RX will not do this. Debounce: This program's debounce routine polls the switches on a F_CPU/64 interval (about 1/990th of a second), it increments or decrements a counter by one. When that counter hits an end value (0 or 20) the switch is consered thrown. As long as the switch doesn't sit in the wrong state for more than 20ms, it should debounce just fine. Overall Theory: I developed this program for my own controller, but bits of it could be useful to someone else, of course. The idea is to have a controller than can adapt between autokap, intervalometer, and fully manual -- including the option to swtich to autokap if it loses contact with the reciever. Pressure altimeter, of course, and an eeprom for logging. It's got pins for a software uart for GPS logging, if necessary. ToDo List: - Internal & External EEPROM - Software UART for GPS - Watchdog Timer Please note that I only code casually, and have actually written quite a bit more in lisp than in C. I'm still a bit afraid of pointers, so some things are written less efficiently than possible. Feel free to offer code snippets and bugfixes & reports back. */ #include #include "device.h" #include #include #include "servo.h" #include "uart.h" #include #include "analog.h" #include "math.h" #include "bitfield.h" #include // Global Variables & defines #define HORIZ 1150 // Horizontal time #define VERT 2100 // Vertical time #define TSTEP 4 // us Step per bit #define INTERVAL_DEF 5400 uint16_t interval=INTERVAL_DEF; // Default interval in 1/990ths unsigned char pan_servo, tilt_servo, shutter_servo; static uint8_t done_flag; uint16_t bytes=0; uint8_t a=0, rcvBuf[20]; uint16_t interval_counter=0; uint16_t timeout_counter=0; uint8_t sw1_bounce=0, sw2_bounce=0; uint8_t mode=0, switches=0; unsigned int tilt_setting=1500; uint8_t global_altitude=0, max_altitude=0; uint8_t do_kap=0; uint32_t calibration=0; // 0 reading for altimeter. uint8_t debug[30]; // Prototypes void autokap(void); void set_tilt(int16_t tilt_value); void pan(uint8_t rate); void shutter(void); void blink(unsigned char i); void longsleep(unsigned char time); // 10ths uint32_t get_altitude(void); void pollswitches(void); int main (void) { uint8_t c=0; int16_t t; uint8_t z=0; // Initialize the Servos servo_init(); shutter_servo = servo_define(_SFR_IO_ADDR(DDRC), _SFR_IO_ADDR(PORTC), 2); pan_servo = servo_define(_SFR_IO_ADDR(DDRC), _SFR_IO_ADDR(PORTC), 3); tilt_servo = servo_define(_SFR_IO_ADDR(DDRC), _SFR_IO_ADDR(PORTC), 4); servo_active(shutter_servo); servo_active(pan_servo); servo_active(tilt_servo); servo_set(shutter_servo, 1500); servo_set(pan_servo, 1500); servo_inactive(pan_servo); servo_set(tilt_servo, HORIZ); // Use servo_set(servo, position); analog_init(); // Initialize Uart (Xbee or FTDI) uartInit(); uartSetBaudRate(115200); _DDRC.B5 = 1; // Shutter LED is an output _DDRB.B1 = 0; _DDRB.B2 = 0; _PORTB.B1 = 1; _PORTB.B2 = 1; blink(1); calibration = get_altitude(); global_altitude = 0; // Start Time0 with 64 prescale cli(); TIMSK0 |= _BV(TOIE0); TCNT0 = 0; TCCR0B = 3; sei(); for(;;) { c=uartGetByte(); if(c == 'T') { t=-1; while(t<0) { t=uartGetByte(); } set_tilt(t); timeout_counter=0; } else if(c == 'S') { shutter(); timeout_counter=0; } else if(c == 'L') { pan('L'); timeout_counter=0; } else if(c == 'R') { pan('R'); timeout_counter=0; } else if(c == 'X') { pan('X'); timeout_counter=0; } else if(c == 'M' && sw1_bounce == 0) // Ignore mode change if already in autokap { mode = 'M'; // M = Manual timeout_counter=0; } else if(c == 'I' && sw1_bounce == 0) // Ignore mode change if already in autokap { mode = 'I'; // I = Intervalometer (trigger shutter @ interval) timeout_counter=0; } else if(c == 'A') { mode = 'A'; // A = Autokap timeout_counter=0; } else if(c == 'N' && sw1_bounce == 0) // Ignore mode change if already in autokap { mode = 'N'; // N = Future timeout_counter=0; } if(mode == 'A' && do_kap == 1) { autokap(); do_kap = 0; // Interval & Autokap } else if(mode == 'I' && do_kap == 1) { shutter(); do_kap = 0; // Intervalometer, but not autokap } else if(do_kap == 1) { do_kap = 0; // Neutral reset } if(sw1_bounce == 100 && switches == 0) { mode = 'A'; // Set to Auto switches = 2; } if(switches == 2 && sw1_bounce == 0) { mode = 'M'; // Set back to manual if we've been in Auto switches = 0; } if(sw1_bounce == 0 && interval != INTERVAL_DEF) { interval = INTERVAL_DEF; } else if(sw1_bounce == 100 && interval == INTERVAL_DEF) { interval = INTERVAL_DEF * 6; } } return 0; } void set_tilt(int16_t tilt_value) { uint16_t time; time = (tilt_value * TSTEP) + HORIZ; if(time > VERT) time = VERT; // This adjusts for the slight adjustment, and still gets us integer math, at some point the rig goes vertical, and then stays there. servo_set(tilt_servo, time); } void shutter(void) { unsigned int count; servo_set(shutter_servo, 1000); // Press button _PORTC.B5 = 1; // Turn on SH-LED for(count=0;count<75;count++) // Wait 0.75s { _delay_ms(10); } servo_set(shutter_servo, 1500); // Neutral servo _PORTC.B5 = 0; // Turn off SH-LED // servo_set(shutter_servo, 2000); /* for(count=0;count<75;count++) { _delay_ms(10); } servo_set(shutter_servo, 1500); */ /* This LED configuration only works if the LED is actually a transistor or relay triggering the shutter. Sits there for 750ms, so it should be good. Might do a couple exposures if hte camera is in rapid fire mode */ uartSendByte('S'); // Tell the Tx that we took a picture. } void pan(uint8_t rate) { if(rate == 'L') { servo_active(pan_servo); servo_set(pan_servo, 1000); } else if(rate == 'R') { servo_active(pan_servo); servo_set(pan_servo, 2000); } else { servo_set(pan_servo, 1500); servo_inactive(pan_servo); } } void autokap() { unsigned int count; shutter(); for(count=0;count<25;count++) // 0.25 seconds { _delay_ms(10); } pan('R'); for(count=0;count<35;count++) // 0.35 seconds { _delay_ms(10); } pan('X'); blink(1); } void blink(unsigned char i) { unsigned char c; unsigned int delay; for(c=0;c> 1); // Shift out the LSB (which is a repeat) alt_data &= 4095; // & with 12 1's pressure = ((alt_data + 389.12) / 36.864) * 10; // kPa * 10 = hPa */ alt_data = analog10(0); // 1024 (1/4 the resolution of the other) pressure = ((alt_data + 97.28) / 9.216) * 10; // millibars altitude = 145366.45 * (1 - pow((pressure / 1013.25), 0.190284)); return altitude; } ISR(TIMER0_OVF_vect) { uint8_t sw1; uint8_t sw2; sw1 = _PINB.B1; sw2 = _PINB.B2; interval_counter++; if( interval_counter == INTERVAL_DEF ) { do_kap = 1; interval_counter=0; } if(sw1==0) { if(sw1_bounce<100) { sw1_bounce++; } } else { if(sw1_bounce>0) { sw1_bounce--; } } if(sw2==0) { if(sw2_bounce<100) { sw2_bounce++; } } else { if(sw2_bounce>0) { sw2_bounce--; } } } int blinkalt(unsigned int i) { int count, delay; for(count = 0; count < i; count++) // 150ms flash for digit { LED_PORT |= (1 << LED0); for(delay = 0; delay < 25; delay++) { _delay_ms(10); } LED_PORT &= ~(1 << LED0); for(delay = 0; delay < 10; delay++) { _delay_ms(10); } } if(i == 0) // 50ms flash for 0 { LED_PORT |= (1 << LED0); for(delay = 0; delay < 5; delay++) { _delay_ms(10); } LED_PORT &= ~(1 << LED0); for(delay = 0; delay < 15; delay++) { _delay_ms(10); } } for(delay = 0; delay < 35; delay++) // 350ms delay between digits { _delay_ms(10); } return 0; }