PlasmaTree

PlasmaTree

ParticleWorld

ParticleWorld

MeteoPlot

MeteoPlot

FlowerPower

FlowerPower

Comming soon


new code
new experiments
new ideas

Contact


Alias: mhepekka
Location: Google Maps
Country: Switzerland
Email: mhe [at] openrise [dot] com
Hide projects

@ 2010 mhepekka | mhe [at] openrise [dot] com

Key features

The machine

bender_328 bender_328 bender_328 bender_328

Sample panorama

resized to 8MP, normally 200MP - 400MP [MP = Megapixel]

Main parts

Main controller

Servo controller

Mechanics

Simplified diagrams

Main controller (86mm x 51mm)

Disadvantage: needs to many of the rare digital pins -> have to change them to analog, then add a sd-card interface for data-storage

Main controller

Servo controller (40mm x 49mm)

Servo controller

Code

Straightforward, not the actual version, but working

bender_328.pde


// bender_328.pde

#include <Streaming.h>
#include <LiquidCrystal.h>
#include <Quadrature.h>
#include <MemoryFree.h>
#include <EEPROM.h>
#include "Param.h"
#include <math.h>
#include "list.h"
#include "config.h"

// PINS

#define ROT_PIN_A 			12 		// rot A
#define ROT_PIN_B 			11		// rot B
#define ROT_PIN_BUTTON 		10 		// rot button
#define BACKLIGHT_PIN 		3		// lcd backlight pin
#define HOME_BUTTON_PIN		2 		// home button
#define SHUTTER_PIN 		13		// camera shutter / focus

// MENU

#define SPHERE_SIZE			7		// size of sphere list
#define MULTIROW_SIZE		10 		// size of multirow list
#define SETUP_SIZE			6		// size of setup list
#define HELPER_SIZE			1		// size of helper list
#define DEBOUNCE_TIME 		500 	// debouncing time for rot button

// Servo

#define TILT_STEPS			1990
#define TILT_FROMTO			180
#define TILT_SERVO_PIN 		1
#define	TILT_DEGDELAY		100		// 29ms / deg
#define TILT_MIDPULSE		3020
#define TILT_SPEED			5

	
#define PAN_STEPS			1723
#define	PAN_FROMTO			360
#define PAN_SERVO_PIN	 	5
#define PAN_DEGDELAY		20		// 14ms / deg speed 4
#define PAN_MIDPULSE		3000			
#define PAN_CHANGEDIROFFSET	5
#define PAN_CHANGEDIRACCURACY false
#define PAN_SPEED			5
#define PAN_DEADANGLE		3

// helper

#define ROBOTER_NAME		"Home"
#define MENU_ACC_TIME		200
#define TILT_DEADANGLE		0 		// todo
#define MENU_GO_HOME_TIME	1000


/******************************************************************************
 * structs / typedef
 *****************************************************************************/

struct Servo_t {
	float pos;
	unsigned char speed;
	boolean on;
	boolean forward;
};

typedef struct panoinfo_t {
	panoinfo_t() : rows(0), cols(0), count(0){}
	uint8_t rows;
	uint8_t cols;
	int count;
} PanoInfo;

// plist

typedef list<Param> plist_t;

// config

template <class T> int EEPROM_write(int ee, const T& value) {
    const byte* p = (const byte*)(const void*)&value;
    int i;
    for (i = 0; i < sizeof(value); i++)
	  EEPROM.write(ee++, *p++);
    return i;
}

template <class T> int EEPROM_read(int ee, T& value) {
    byte* p = (byte*)(void*)&value;
    int i;
    for (i = 0; i < sizeof(value); i++)
	  *p++ = EEPROM.read(ee++);
    return i;
}

config conf;
Servo_t panServo;
Servo_t tiltServo;


/******************************************************************************
 * Menu structure
 *****************************************************************************/
 
// Common Param
 
RangeParam 	common_exposure_time("Exp [ms]", 0, 5000, 25, 500);
RangeParam	common_hdr("HDR [1-5]", 1, 5, 2, 1);
RangeParam	common_f("f [mm]", 10, 1000, 1, 10);
RangeParam 	common_pan_overlap("Pan [%]", 25, 60, 5, 30);
RangeParam 	common_tilt_overlap("Tilt [%]", 25, 60, 5, 30);
 
// Sphere panorama

DicItem		sphere_zenith_yes("Yes", 1);
DicItem		sphere_zenith_no("No", 0);
DicItem		sphere_zenith_both("Plus", 2);
DicItem		*sphere_zenith_items[3] = {&sphere_zenith_no, &sphere_zenith_yes, &sphere_zenith_both};
DicParam	sphere_zenith("Zenith", sphere_zenith_items, 3, 1);
 
VoidParam 	sphere_start;
Param 		*Param_sphere[SPHERE_SIZE] = 
			{
				&common_f,
				&common_pan_overlap,
				&common_tilt_overlap,
				&common_exposure_time,
				&common_hdr,
				&sphere_zenith,
				&sphere_start
			};

// Multirow panorama

RangeParam	multirow_pan_from("X1", 0, 360, 1, 0);
RangeParam	multirow_tilt_from("Y1", -90, 90, 1, 0);
RangeParam	multirow_pan_to("X2", 0, 360, 1, 10);
RangeParam	multirow_tilt_to("Y2", -90, 90, 1, 30);
VoidParam	multirow_start;				
Param		*Param_multirow[MULTIROW_SIZE] =
			{
				&common_f,
				&multirow_tilt_from,
				&multirow_tilt_to,
				&multirow_pan_from,
				&multirow_pan_to,
				&common_pan_overlap,
				&common_tilt_overlap,
				&common_exposure_time,
				&common_hdr,
				&multirow_start				
			};

// SETUP

DicItem 	setup_sensor_10("1.0x", 100);
DicItem		setup_sensor_13("1.3x", 125);
DicItem 	setup_sensor_15("1.5x", 151);
DicItem 	setup_sensor_16("1.6x", 162);
DicItem 	*setup_sensor_items[4] = {&setup_sensor_10, &setup_sensor_13, &setup_sensor_15, &setup_sensor_16};
DicParam	setup_sensor("Sensor", setup_sensor_items, 4, 3);
RangeParam 	setup_lcd_Backlight("LCD", 0, 255, 5, 10);
InfoParam	setup_save_config("Save", "Ok", 0);
RangeParam	setup_tripod_wait("Tripod [ms]", 0, 2000, 100, 0);
RangeParam  setup_countdown("Timer [s]", 0, 10, 1, 0);
RangeParam	setup_change_direction("Dir", 0, 1, 1, 0);
Param		*Param_setup[SETUP_SIZE] =
			{
				&setup_sensor,
				&setup_lcd_Backlight,
				&setup_tripod_wait,
				&setup_countdown,
				&setup_change_direction,
				&setup_save_config
			};

// pstore / plists

plist_t l_setup("Setup", Param_setup, false, SETUP_SIZE);
plist_t l_sphere("Sphere", Param_sphere, false, SPHERE_SIZE);
plist_t l_multirow("Multirow", Param_multirow, false, MULTIROW_SIZE);
plist_t *plists[3] = {&l_setup, &l_sphere, &l_multirow}; 
list<plist_t> pstore(plists, false, 3);

// LCD

LiquidCrystal lcd(9, 8, 7, 6, 5, 4); // rs, enable, d4,d5,d6,d7

// ROTARY ENCODER

Quadrature rot(ROT_PIN_A, ROT_PIN_B);

// HELPER

long last_action = 0;
byte mode = 0;
boolean changed = false;
Param *p;
PanoInfo pi;
boolean switcher = false;

//**************************************************************
// SETUP
//**************************************************************

void setup() {
	
	// load config
	
	EEPROM_read(0, conf);
	setup_lcd_Backlight.value(conf.setup_lcd_Backlight);
	setup_sensor.value(conf.setup_sensor);
	setup_tripod_wait.value(conf.setup_tripod_wait);
	setup_change_direction.value(conf.setup_change_direction);
	common_exposure_time.value(conf.common_exposure_time);
	common_hdr.value(conf.common_hdr);
	common_pan_overlap.value(conf.common_pan_overlap);
	common_tilt_overlap.value(conf.common_tilt_overlap);
	common_f.value(conf.common_f);
	sphere_zenith.value(conf.sphere_zenith);
	setup_countdown.value(conf.setup_countdown);
	
	// init servo

	panServo.pos = 0.0;
	tiltServo.pos = -90.0;
	
	// lcd setup
	
	lcd.begin(16,2);
	pinMode(BACKLIGHT_PIN, OUTPUT);
	analogWrite(BACKLIGHT_PIN, setup_lcd_Backlight.value());

	// Init home button

	pinMode(HOME_BUTTON_PIN, INPUT);
	digitalWrite(HOME_BUTTON_PIN, HIGH); // activate internal pullup    

	// Init RotaryEnocder button

	pinMode(ROT_PIN_BUTTON, INPUT);
	digitalWrite(ROT_PIN_BUTTON, HIGH); // activate internal pullup  
	
	// Init shutter pin
	
	pinMode(SHUTTER_PIN, OUTPUT);

	// Event handling

	sphere_start.onSelectHandler 			= &sphere_start_onSelect;
	sphere_start.onRenderHandler	 		= &sphere_start_onRender;
	multirow_start.onSelectHandler 			= &multirow_start_onSelect;
	multirow_start.onRenderHandler 			= &multirow_start_onRender;
	multirow_pan_from.onChangeHandler		= &multirow_pan_onChange;
	multirow_tilt_from.onChangeHandler		= &multirow_tilt_onChange;
	multirow_pan_to.onChangeHandler			= &multirow_pan_onChange;	
	multirow_tilt_to.onChangeHandler		= &multirow_tilt_onChange;	
	multirow_pan_from.onSelectHandler		= &multirow_pan_onChange;
	multirow_tilt_from.onSelectHandler		= &multirow_tilt_onChange;
	multirow_pan_to.onSelectHandler			= &multirow_pan_onChange;
	multirow_tilt_to.onSelectHandler		= &multirow_tilt_onChange;			
	setup_lcd_Backlight.onChangeHandler		= &setup_lcd_Backlight_onChange;
	setup_save_config.onSelectHandler		= &setup_save_config_onSelect;
		
  	// Init Param *p

	p = pstore.first() -> first();

    // Memory usage

	lcd << freeMemory();
			
	// Serial
	
	Serial.begin(9600);

	// Init servo
	
	setServoOn(false, false);

}

//**************************************************************
// LOOP
//**************************************************************

void loop() {
  
  	char rot_pos = rot.position();
	if (rot_pos != 0) {
		
		// calc acceleration
		
		int steps = MENU_ACC_TIME / (millis() - last_action) + 1;
		switch(mode) {
			case 0:
				p = (rot_pos > 0) ? pstore.next() -> first() : pstore.prev() -> first();			
				break;
			case 1:
				p = (rot_pos > 0) ? pstore.item() -> next() : pstore.item() -> prev();
				break;
			case 2:
				if (rot_pos > 0) {
					p -> inc(steps);  
				} else {
					p -> dec(steps);
				}
			break; 
		} 
		changed = true;
		rot.position(0);
		last_action = millis();
	}
  
  // Rot button pressed

	if (digitalRead(ROT_PIN_BUTTON) == LOW && millis() - last_action > DEBOUNCE_TIME) {
		switch(mode) {
			case 0:
				mode = 1;
				break;
			case 1:
				mode = 2;
				p -> select();
				break;
			case 2:
				mode = 1;
				break; 
		}
		changed = true;	
		last_action = millis();
	}
  
	// Homebutton pressed

	if (digitalRead(HOME_BUTTON_PIN) == LOW  && millis() - last_action > DEBOUNCE_TIME) { 
		changed = true;
		mode = 0;
		p = pstore.item() -> first();
		last_action = millis();
	}
    
	// changed event

	if (changed) {
		lcd.clear();
		if (mode == 0) {
			lcd << ROBOTER_NAME;
			lcd.setCursor(0,1);
			lcd.write(0b01111110);
			lcd << " " <<  pstore.item() -> name;
		} else {
			if (!p -> onRenderHandler) {
				lcd << p -> name;
				lcd.setCursor(0,1);
				lcd.write(0b01111110);
				lcd << " " << p -> text();
				if (mode == 2) {
					lcd.cursor();
				} else {
					lcd.noCursor(); 
				}
			} else {
				p -> render();
			}
		}
		changed = false;
	}            
}

//**************************************************************
// Event handling
//**************************************************************

void sphere_start_onSelect(Param *p) {	
	
	// go 0,-90
	
	delay(1000);
	setServoOn(true, true);
	setServoSpeed(PAN_SPEED, TILT_SPEED);
	setServoToOrigin(false);
	
	// start countdown
	
	countdown(setup_countdown.value());
	
	// do pano
	
	doSphericPanorama(true);
	
	// go 0, -90
	
	setServoToOrigin(false);
	delay(1000);
	waitForKeyPress(HOME_BUTTON_PIN);
	setServoOn(false, false);
}

void multirow_start_onSelect(Param *p) {

	// middle / middle

	delay(1000);
	setServoOn(true, true);
	setServoSpeed(PAN_SPEED, TILT_SPEED);
	moveTo((float)multirow_pan_from.value(), (float)multirow_tilt_to.value(), true);
	setServoSpeed(10, TILT_SPEED);

	// start countdown

	countdown(setup_countdown.value());
		
	// do pano	
	
	doMultirow(true);
	
	// go 0, -90
	
	setServoToOrigin(false);
	delay(1000);
	waitForKeyPress(HOME_BUTTON_PIN);
	setServoOn(false, false);
}

void sphere_start_onRender(Param *p) {
	lcd.clear();
	doSphericPanorama(false);
	lcd_PanoInfo();
}

void multirow_start_onRender(Param *p) {
	lcd.clear();
	doMultirow(false);
	lcd_PanoInfo();
}

void lcd_PanoInfo() {
	lcd << "Start";
	lcd.setCursor(0, 1);
	lcd.write(0b01111110);
	lcd << " " << "#" << pi.count << " [" << (int)pi.cols << "x" << (int)pi.rows << "]";
}

void setup_lcd_Backlight_onChange(Param *p) {
	analogWrite(BACKLIGHT_PIN, p -> value());
}

void setup_save_config_onSelect(Param *p) {

	conf.setup_lcd_Backlight	= setup_lcd_Backlight.value();
	conf.setup_sensor			= setup_sensor.index();
	conf.setup_countdown		= setup_countdown.value();
	conf.setup_tripod_wait		= setup_tripod_wait.value();
	conf.setup_change_direction = setup_change_direction.value();
	conf.common_f				= common_f.value();
	conf.common_hdr				= common_hdr.value();
	conf.common_exposure_time	= common_exposure_time.value();
	conf.common_pan_overlap		= common_pan_overlap.value();
	conf.common_tilt_overlap	= common_tilt_overlap.value();
	conf.sphere_zenith			= sphere_zenith.value();

	//write to EEPROM

	EEPROM_write(0, conf);
	mode = 0;

}

void multirow_pan_onChange(Param *p){
	setServoSpeed(PAN_SPEED, TILT_SPEED);
	setServoOn(true, true);
	moveTo((float)(p -> value()), tiltServo.pos, false);
}

void multirow_tilt_onChange(Param *p){
	setServoSpeed(PAN_SPEED, TILT_SPEED);
	setServoOn(false, true);
	moveTo(panServo.pos, (float)(p -> value()), false);
}

//**************************************************************
// countdown
//**************************************************************

boolean countdown(int s) {
	
	for (int i = 0; i < s; i+=1){
		lcd.clear();
		lcd << "Start in :" << s - i;
		delay(1000);
	} 
}

//**************************************************************
// doSphericPanorama
//**************************************************************

void doSphericPanorama(boolean mode) {

	// fov

	int tilt_fov = lens_fov(sensor_width(), common_f.value());
	int pan_fov = lens_fov(sensor_height(), common_f.value());
	
	// pan_from
	
	int pan_from = 0;
	
	// pan_to
	
	int pan_to = 360 - pan_fov * (1- (float)common_pan_overlap.value() / 100) +1; // round
		
	// tilt_from
	
	int tilt_from = -90 + TILT_DEADANGLE + (float)tilt_fov / 2 - 1;
	if (sphere_zenith.value() == 1)	{
		tilt_from += (int)((float)pan_fov / 2) * ((float)1 - (float)common_pan_overlap.value() / 100);
	}
	
	// tilt_to
	
	int tilt_to = 90 - (float)tilt_fov / 2 + 1;

	// Serial << tilt_from << "_" << tilt_to << endl;

	// zenith
	
	if ((sphere_zenith.value() == 1 | sphere_zenith.value() == 2) && mode) {
		moveTo(panServo.pos, -90, true);
		takePhoto();
		delayAbort(HOME_BUTTON_PIN, common_exposure_time.value());
	}
	
	// let's do it
	
	doPanorama(pan_from, pan_to, tilt_from, tilt_to, true, mode);
	
}

//**************************************************************
// doMultirow
//**************************************************************

void doMultirow(boolean demo) {

	int pan_from = multirow_pan_from.value();
	int tilt_from = multirow_tilt_from.value();
	int pan_to = multirow_pan_to.value();
	int tilt_to = multirow_tilt_to.value();
	
	// let's do it
	
	doPanorama(pan_from, pan_to, tilt_from, tilt_to, false, demo);
	
}

//**************************************************************
// doPanorama
//**************************************************************

void doPanorama(int pan_from, int pan_to, int tilt_from, int tilt_to, boolean fov_correction, boolean mode) {

	// init pi | mode == true -> take photos
	
	if (!mode)
	{	
		pi.cols = 0;
		pi.rows = 0;
		pi.count = 0;
	}

	// lens fov
	
	float sensor_Width = sensor_width();
	float sensor_Height = sensor_height();
	float tilt_fov = lens_fov(sensor_Width, common_f.value());
	float pan_fov = lens_fov(sensor_Height, common_f.value());
	float tilt_fov_overlap = fov_overlap(sensor_Width, common_f.value(), common_tilt_overlap.value());
	float pan_fov_overlap = fov_overlap(sensor_Height, common_f.value(), common_pan_overlap.value());

	// Serial << pan_fov << "," << pan_fov_overlap << "," << tilt_fov << "," << tilt_fov_overlap << endl;

	// from - to
	
	int pan_fromto = abs(pan_to - pan_from);
	int tilt_fromto = abs(tilt_to - tilt_from);
	
	// tilt
	
	int tilt_count = step_count(tilt_fromto, tilt_fov_overlap);
	float tilt_step = step_angle(tilt_fromto, tilt_count);
	
	//Serial << tilt_fov << "," << tilt_fov_overlap << "," << tilt_count << "," << tilt_step << endl;
	
	// helper

	boolean forward = true;
	boolean abort = false;
	float pan_pos = (float)pan_from;
	int counter = 1;
	unsigned char v = 0;
	unsigned char h = 0;
	unsigned char i = 0;
	float tilt_pos;
		
	// loop

	while (v <= tilt_count && !abort) {
	
		// change tilt_pos
	
		if (tilt_from < tilt_to){
			tilt_pos = tilt_from + (float)v * tilt_step;
		} else {
			tilt_pos = tilt_from - (float)v * tilt_step;
		}		

		// recalc pan step 

		float corr = fov_correction ? cos(tilt_pos * PI / 180) : 1;
		int pan_count = step_count(pan_fromto, pan_fov_overlap / corr);
		float pan_step = step_angle(pan_fromto, pan_count);
		
		// Serial << pan_fov << "," << pan_fov_overlap << "," << pan_count << "," << pan_step << endl;	
		
		// pan loop
		
		while (h <= pan_count && !abort) {			

			pi.cols = h + 1> pi.cols ? h + 1 : pi.cols;

			// change pan_pos

			if (forward) {
				if (pan_from < pan_to) {
					pan_pos = pan_from + (float)h * pan_step;
				} else {
					pan_pos = pan_from - (float)h * pan_step;
				}			
			} else	{
				if (pan_from < pan_to) {
					pan_pos = pan_to - (float)h * pan_step;
				} else {
					pan_pos = pan_to + (float)h * pan_step;
				}
			}
			
			if (mode) {			
				
				// lcd update
					
				lcd.clear();
				lcd << "Img " << counter << "|" << pi.count;
				//lcd.setCursor(0, 1);				
				//lcd << pan_pos;
				//lcd.write(0b11011111);
				//lcd << "|" << tilt_pos;
				//lcd.write(0b11011111);
				
				// goto pos
				
				abort = moveTo(pan_pos, tilt_pos, true);
				
				// tripod stabilization wait
				
				if (!abort)
					abort = delayAbort(HOME_BUTTON_PIN, setup_tripod_wait.value());
				
				while (i < common_hdr.value() && !abort){
					
					// take photo
					
					if (!abort){
						takePhoto();
					}

					// exposure time 
					
					if (!abort){
						abort = delayAbort(HOME_BUTTON_PIN, common_exposure_time.value());	
					}
					counter++;
					i++;
				} 
			} else {
				counter += common_hdr.value();
			}
			h++;
			i = 0;
		}
		v++;
		h = 0;
		forward = !forward;
	}

	// save pi
	
	pi.count = counter - 1;
	pi.rows = tilt_count + 1;
}

int tiltPulseWidth(float pos) {
	int startPulse = TILT_STEPS/ 2 + TILT_MIDPULSE;
	return startPulse - (float)TILT_STEPS / (float)TILT_FROMTO * (90 + pos) + 1; // round + 1
}

int panPulseWidth(float pos) {
	int startPulse = PAN_STEPS / 2 + PAN_MIDPULSE;
	return startPulse - (float)PAN_STEPS / (float)PAN_FROMTO * pos + 1; // round + 1
}

//**************************************************************
// moveTo
//**************************************************************

boolean moveTo(float pan_pos, float tilt_pos, boolean waitForMovement) {
	
	boolean abort = false;
	int move_delay = 0;
	int move_pan_delay = 0;
	int move_tilt_delay = 0;
	int pulseStep = 0;
	int step = 0;
	
	// pan move
	
	if (panServo.on) {	
		//boolean forward = panServo.pos > pan_pos ? true : false;
		//if (forward != panServo.forward && setup_change_direction.value() == 1) { // PAN_CHANGEDIRACCURACY) {
			
			// change direction
			
		//	float dead_angle_step = forward ? panServo.pos + PAN_CHANGEDIROFFSET : panServo.pos - PAN_CHANGEDIROFFSET;
		//	servoPut(PAN_SERVO_PIN, panPulseWidth(dead_angle_step));
		//	delay(200); 
		//
		//}	
		//panServo.forward = forward;
		servoPut(PAN_SERVO_PIN, panPulseWidth(pan_pos));
		
		// calculate time
		
		step = abs(pan_pos - panServo.pos);
		move_pan_delay = PAN_DEGDELAY * step;  	// fullspeed move
		move_pan_delay += 2000;					// startup + slowdown
		
	}

	// tilt move
	
	if (tiltServo.on) {
		if (tiltServo.pos > tilt_pos) {
			
			// go down
			
			servoSpeed(TILT_SERVO_PIN, 2);

		} else {
			servoSpeed(TILT_SERVO_PIN, TILT_SPEED);
		}
		servoPut(TILT_SERVO_PIN,  tiltPulseWidth(tilt_pos));
		move_tilt_delay = TILT_DEGDELAY * abs(tilt_pos - tiltServo.pos);
	}

	// calculate delay

	move_delay = max(move_pan_delay, move_tilt_delay);
	
	// delay
	
	if (waitForMovement) {
		abort = delayAbort(HOME_BUTTON_PIN, move_delay);
	}
	
	// save position
	
	panServo.pos = pan_pos;
	tiltServo.pos = tilt_pos;
	
	return abort;
}

//**************************************************************
// delay with abort
//**************************************************************

boolean delayAbort(unsigned char pin, int ms) {
	last_action = millis();
	boolean abort = false;
	while (last_action + ms > millis() && !abort) {
		if (digitalRead(pin) == LOW) {
			abort = true;
		}
	}
	return abort;
}

//**************************************************************
// Calculate field of view
//**************************************************************

float fov_overlap(float l, int f, int overlap) {
	float fov = (360 / PI) * atan(l / (float)(2 * f));// + 1;
	return fov * (1 - (float)overlap / 100);// + 1;
}

float lens_fov(float l, int f) {
	return (360 / PI) * atan(l / (float)(2 * f)); // +1;
}

int lens_step(int angle, int fov_overlap) {
	return (angle / fov_overlap) + 1;
}

float step_angle(int angle, int step) {
	return angle / (float)step;
}

int step_count(int angle, float fov) {
	return ((float)angle / fov) + 1;
}

//**************************************************************
// Wait for key press
//**************************************************************

void waitForKeyPress(char pin) {
	while (digitalRead(pin) == HIGH){}
}

//**************************************************************
// displayErrorMsg
//**************************************************************

void displayErrorMsg(char pin) {
	lcd.clear();
	lcd << "Error";
	waitForKeyPress(pin);
}

//**************************************************************
// takePhoto
//**************************************************************

void takePhoto() {
	digitalWrite(SHUTTER_PIN, HIGH);
	delay(200);
	digitalWrite(SHUTTER_PIN, LOW);
}

//**************************************************************
// setServoSpeed
//**************************************************************

void setServoSpeed(unsigned char pan_speed, unsigned char tilt_speed) {
	panServo.speed = pan_speed;
	tiltServo.speed = tilt_speed;
	servoSpeed(PAN_SERVO_PIN, pan_speed); 
	servoSpeed(TILT_SERVO_PIN, tilt_speed);
}

//**************************************************************
// setServoToOrigin
//**************************************************************

void setServoToOrigin(boolean wait) {
	moveTo(0.0, -90.0, wait);
}

//**************************************************************
// setServoOn
//**************************************************************

void setServoOn(boolean pan_on, boolean tilt_on) {
	panServo.on = pan_on;
	tiltServo.on = tilt_on;
	if (!pan_on) {
		servoOff(PAN_SERVO_PIN);
	}
	if (!tilt_on) {
		servoOff(TILT_SERVO_PIN);
	}
}

//**************************************************************
// Servo put
//**************************************************************

void servoPut(unsigned char Servo, unsigned int angle) {
	Serial.print(0x80,BYTE); //start byte
	Serial.print(0x01,BYTE); //device id
	Serial.print(0x04,BYTE); //command number
	Serial.print(Servo,BYTE); //Servo number
	//Convert the angle data into two 7-bit bytes
	Serial.print(((angle>>7)&0x3f),BYTE); //data1
	Serial.print((angle&0x7f),BYTE); //data2
}

//**************************************************************
// Servo off
//**************************************************************

void servoOff(unsigned char Servo) {
	Serial.print(0x80,BYTE);//start byte
	Serial.print(0x01,BYTE);//device id
	Serial.print(0x00,BYTE);//command number
	Serial.print(Servo,BYTE);//Servo number
	Serial.print(0x0f,BYTE);//data1 (turn Servo off, keep full range)
}

//**************************************************************
// Servo speed
//**************************************************************

void servoSpeed(unsigned char Servo, unsigned char speedcmd) {
  	speedcmd=speedcmd&0x7f;//take only lower 7 bits of the speed
	Serial.print(0x80,BYTE);//start byte
	Serial.print(0x01,BYTE);//device id
	Serial.print(0x01,BYTE);//command number
	Serial.print(Servo,BYTE);//Servo number
	Serial.print(speedcmd,BYTE);//data1
}

//**************************************************************
// Servo controller reset
//**************************************************************

void servoReset() {
   Serial.print(0x80,BYTE); //start byte
   Serial.print(0x02,BYTE); //configuration command
   Serial.print(0x00,BYTE); //Servo number range
   while(1);//stop here
}

float sensor_width()
{
	//return 22.2;
	return 36.00 / setup_sensor.value() *100;
}

float sensor_height()
{
	//return 14.8;
	return 24.00 / setup_sensor.value() * 100;
}

void serialFreeMemory()
{
	Serial << freeMemory() << endl;
}



config.h

#ifndef config_h
#define config_h

struct config {
	unsigned char setup_sensor;
	unsigned char setup_lcd_Backlight;
	int setup_tripod_wait;
	int setup_countdown;
	int setup_change_direction;
	int common_hdr;
	int common_exposure_time;
	int common_f;
	int common_pan_overlap;
	int common_tilt_overlap;
	int sphere_zenith;
};

#endif

list.h

#ifndef list_h
#define list_h

//**************************************************************
// list
//**************************************************************

template<class T> class list {
	public:
	
		list(T **items, bool cyclic, uint8_t size) {
			_index = 0;
			_cyclic = cyclic;
			_items = items;
			_size = size;
		};
		
		list(char *n, T **items, bool cyclic, uint8_t size) {
			name = n;
			_index = 0;
			_cyclic = cyclic;
			_items = items;
			_size = size;
		};

		T *first() {
			return _item(0);
		};
		
		T *next() {
			uint8_t tmp = _index;
			if (tmp == _size -1) {
				if (_cyclic) tmp = 0;
			} else {
				tmp++;
			}
			return _item(tmp);
		};
		
		T *prev() {
			uint8_t tmp = _index;
			if (tmp == 0) {
				if (_cyclic) tmp = _size -1;
			} else {
				tmp--;
			}
			return _item(tmp);		
		};
		
		T *last() {
			return _item(_size -1);
		};
		
		T *item() {
			return _item(_index);
		};
	
		bool hasPrev() {
			if (!_cyclic) {
				return (_index > 0) ? true: false;
			} else {
				return true;
			}
		};
	
		bool hasNext() {
			if (!_cyclic) {
				return (_index < _size -1) ? true: false;
			} else {
				return true;
			}
		};
	
		int size() {
			return _size;
		}
	
		int pos() {
			return _index + 1;
		}
	
		char *name;
	
	private:
		T *_item(uint8_t index) {
			_index = index;
			return (_items) ? _items[index] : 0;
		};
		T **_items;
		uint8_t _size;
		uint8_t _index;
		bool _cyclic;
};

#endif

Param.h

#ifndef Param_h
#define Param_h

#include <WProgram.h>
#include <inttypes.h>

class Param;
typedef void (*OnChange)(Param*);
typedef void (*OnRender)(Param*);
typedef void (*OnSelect)(Param*);

//**************************************************************
// Param BaseClass
//**************************************************************

class Param {

	public:

		Param();
	
		// Events
		
		OnChange 	onChangeHandler;
		OnSelect	onSelectHandler;
		OnRender	onRenderHandler;
	
		// Virtual
	
		virtual int value() 		= 0;
		virtual char *text() 		= 0;
		virtual void inc(uint8_t) 	= 0;
		virtual void dec(uint8_t) 	= 0;
			
		// Methods
		
		void select();
		void render();
		
		// Name
		
		const char *name;
	
};

//**************************************************************
// RangeParam
//**************************************************************

class RangeParam : public Param {
	public:

		RangeParam(const char*, int, int, int, int);
		int value();
		char *text();
		void inc(uint8_t);
		void dec(uint8_t);
		void value(int);

	private:

		int _from, _to, _step, _value;
};

//**************************************************************
// VoidParam
//**************************************************************

class VoidParam : public Param {
	public:
	
		VoidParam();
		int value();
		char *text();
		void inc(uint8_t){};
		void dec(uint8_t){};
};

//**************************************************************
// InfoParam
//**************************************************************

class InfoParam : public Param {
	public:
	
		InfoParam(const char*, char*, int);
		int value();
		void inc(uint8_t){};
		void dec(uint8_t){};
		char *text();
	
	private:
	
		int _value;
		char *_msg;
};

//**************************************************************
// DicItem
//**************************************************************

struct DicItem {
	DicItem(char*, int);
	char *name;
	int value;		
};

//**************************************************************
// DicParam
//**************************************************************

class DicParam : public Param {
	public:
	
		DicParam(char*, DicItem**, uint8_t, uint8_t);
		int value();
		char *text();
		void inc(uint8_t);
		void dec(uint8_t);
		void value(int);
		uint8_t index();
	
	private:
	
		uint8_t _index;
		uint8_t _size;
		DicItem **_items;
};

#endif

Param.cpp

#include "Param.h"

//**************************************************************
// Param BaseClass
//**************************************************************

Param::Param() {
	onChangeHandler = 0;
	onSelectHandler = 0;
	onRenderHandler = 0;
}

void Param::select() {
	if (onSelectHandler)
		(onSelectHandler)(this);
}

void Param::render() {
	if (onRenderHandler)
		(onRenderHandler)(this);
}

//**************************************************************
// RangeParam
//**************************************************************

RangeParam::RangeParam(const char *n, int from, int to, int step, int value): _from(from), _to(to), _step(step), _value(value) {
	name = n;
}

int RangeParam::value() {
	return _value;
}

char *RangeParam::text() {
	static char buffer[7];
	sprintf(buffer, "%i", _value);
	//itoa(_value, buffer, 10);
	return buffer;
}

void RangeParam::inc(uint8_t c) { 
	int tmp = _value;
	_value = min(_to, tmp + _step * c);
	if (onChangeHandler && _value != tmp)
		(onChangeHandler)(this); //OnChange ChangeHandler = onChangeHandler;
}

void RangeParam::dec(uint8_t c) {
	int tmp = _value;
	_value = max(_from, tmp - _step * c);
	if (onChangeHandler && _value != tmp)
		(onChangeHandler)(this); //OnChange ChangeHandler = onChangeHandler;
}

void RangeParam::value(int v) {
	_value = v;
}

//**************************************************************
// VoidParam
//**************************************************************

VoidParam::VoidParam() {
	name = 0;
}
 
int VoidParam::value(){ return 0;}
char *VoidParam::text(){ return 0;}

//**************************************************************
// InfoParam
//**************************************************************

InfoParam::InfoParam(const char *n, char *msg, int value):_value(value), _msg(msg) {
	name = n;
}

int InfoParam::value() {
	return _value;
}

char *InfoParam::text() {
	return _msg;
}

//**************************************************************
// DicItem
//**************************************************************

DicItem::DicItem(char *n, int v):name(n), value(v){}

//**************************************************************
// DicParam
//**************************************************************

DicParam::DicParam(char *n, DicItem **items, uint8_t size, uint8_t index):_items(items), _index(index), _size(size) {
	name = n;
}

void DicParam::value(int index) {
	_index = index;
}

int DicParam::value() {
	return _items[_index] -> value;
}

char *DicParam::text() {
	return _items[_index] -> name;
}

void DicParam::inc(uint8_t count) {
	int tmp = _index;
	_index = min(_size -1, tmp + 1);
	if (onChangeHandler && _index != tmp)
		(onChangeHandler)(this);
}

void DicParam::dec(uint8_t count) {
	int tmp = _index;
	_index = max(0, tmp -1);
	if (onChangeHandler && _index != tmp)
		(onChangeHandler)(this);
}

uint8_t DicParam::index() {
	return _index;
}