@ 2010 mhepekka | mhe [at] openrise [dot] com
Key features
- Weight: 1.4kg (3.1 lbs) including batteries and quick release system
- Small: 23cm x 30cm x 8cm, 9.1in x 11.8in x 3.2in (length x height x width)
- Panorama: Spheric and multirow panorama
- Software: "Sony"-like input interface with a rotary encoder
- User settings: EEPROM for user settings
- High torque: nearly 10Nm (Tilt servo with metal gear)
The machine
Sample panorama
resized to 8MP, normally 200MP - 400MP [MP = Megapixel]
Main parts
Main controller
- Arduino Pro Mini ATmega328
- HD44870-compatible LCD (Ebay)
- RotaryEncoder with push function for user-interaction
- Trimmer for LCD contrast
- Pushbutton for "back" function
Servo controller
- Pololu servo controller - Serial servo controller
- Zehner diode for reverse polarity protection
- Status LED
- Optocoupler for shutter control and camera protection
- On-off switch
Mechanics
- 2 Hitec servos HS-785HB
- 15.5mm x 15.5mm aluminium tubes
- At least 40 screws
- Gears, tubings, shaftings, hubs, adaptors and screws form servocity
- Battery housing for 5xAA
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
Servo controller (40mm x 49mm)
Code
Straightforward, not the actual version, but working
- bender_328.pde - The main program
- config.h - Datastructure for EEPROM
- list.h - Generic list (used for the menu structure)
- param.h - Headerfile for param
- param.cpp - Datastructure for LCD navigation / menu
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;
}



