--[[ @title PictUAVRuby for install / config, see: http://uthere.com/products/ruby/documentation/operation/photography/technical/camera_photography_control_technical_canon_chdk.html for end user operation, see: http://uthere.com/products/ruby/documentation/operation/photography/camera_photography_control.html --]] props = require "propcase" version = "777 (interval)" cfg_shutter_trigger_rapid_continuous = true -- instead of waiting for trigger pulses, just shoot continuously at maximum rate cfg_shutter_trigger_full = true -- instead of keeping it half-pressed, do a simple call to shoot() for full trigger press and release for each shot, as in original interval script function get_mode_is_record() is_record,is_video,mode_number = get_mode() return is_record end function lens_extend() -- (** does this actually extend the lens now, or is a prefocus or camera trigger required?) -- make sure we're in record mode, switch to record mode if necessary -- ok to call this repeatedly if(not get_mode_is_record()) then logfile_add("extend","start") set_record(1) timeElapsedSecs=0; while((not get_mode_is_record()) and (timeElapsedSecs<5)) do sleep(1000) timeElapsedSecs = timeElapsedSecs+1 end if(not get_mode_is_record()) then logfile_add("extend","fail **") else logfile_add("extend","success") end end end function lens_retract() -- NOTE: In the camera, must set Menu: Tools: Lens Retract: 0 sec -- (also set Power saving: Auto Power Down to 'OFF') if(get_mode_is_record()) then logfile_add("retract","start") release("shoot_half") set_record(0) -- the only known way to retract is to put it into play mode and wait for a minute timeElapsedMSecs=0; while((get_mode()~=0) and (timeElapsedMSecs<5000)) do sleep(500) timeElapsedMSecs = timeElapsedMSecs+500 end if(get_mode_is_record()) then logfile_add("retract","fail **") else logfile_add("retract","success") -- the lens actually hasn't retracted, but we're successfully in "play" mode end end end function prefocus() -- switch to autofocus mode, pre-focus, then go to manual focus mode --- question - if we're set up for manual, is there any pre-focus needed/ wanted? local focused = false local try = 1 lens_extend() -- ok to call this repeatedly logfile_add("prefocus","start") press("shoot_half") timeElapsedMSecs=0; while((get_shooting()==false) and (timeElapsedMSecs<5000)) do sleep(50) timeElapsedMSecs = timeElapsedMSecs+50 end if(get_shooting() == false) then logfile_add("prefocus","fail **") else focused = true set_aflock(1) logfile_add("prefocus","success") end -- don't release "shoot_half".. keep it held at half to minimize delay when triggering shutter end function shutter_trigger() -- assume that prefocus() has already been called or is in manual focus infinity -- (leave it up to Ruby firmware to make sure this happens) --could turn on led below to test timing --set_led (7,1) --sleep (10) --set_led (7,0) -- based on http://chdk.wikia.com/wiki/Fast_Shooter_Intervalometer ecnt=get_exp_count() shutter_trigger_start_time = get_tick_count() -- NO don't generate a log entry here - it slows things down by 150 - 170 usecs(due to flush?) --click("shoot_full_only")-- NO click this gets stuck on second picture for some reason // must press then release if(cfg_shutter_trigger_full == true) then shoot() else press("shoot_full_only") -- not "shoot_full". shoot_full_only doesn't release the button. It leasvves "shoot_hal" in plaace repeat sleep(20) until(get_exp_count()~=ecnt) -- takes between 380 and 1130 ticks (why the variance?) release("shoot_full_only") end ecnt=get_exp_count() logfile_add("shutter",shutter_trigger_start_time.."\t"..ecnt.."\t"..frame.."\t".."IMG_" .. string.format('%04d',ecnt) .. ".jpg") frame = frame + 1 end function dump_propCase() -- see also http://chdk.wikia.com/wiki/PropertyCaseDump logfile_add("Properties dump") filepath = "A/DCIM/pictuavRuby_properties_dump.txt" dump_io = io.open(filepath, "a") if(dump_io:seek() == 0) then dump_io:write("This is a dump of the state and configurable values of this camera.\n") dump_io:write("This can be useful for determining if the camera is properly configured..\n") dump_io:write("--------------------------------------------------------------------------------.\n") end logfile_add_gut(dump_io,"Begin properties ") for i= 0, 300 do v = get_prop(i) dump_io:write("Property\t"..i.."\t"..v.."\n") end logfile_add_gut(dump_io,"Begin params ") for i=0,get_flash_params_count()-1 do s,n = get_parameter_data(i) dump_io:write(i,"\t") if s then -- string as hex for j=1,s:len() do dump_io:write(string.format("0x%02x ",s:byte(j))) end -- string quoted dump_io:write(string.format("[%q]",s)) -- as number, if available if n then dump_io:write(string.format(" 0x%x %d",n,n)) end else dump_io:write("nil") end dump_io:write("\n") end dump_io:close() end -- LOG FILES: -- maintain a permanent cumulative master log file and also create a log file in the current image directory which just logs events corresponding to those images logfile_master_io = nil logfile_imagedir_io = nil function logfile_init_gut(logfile_io) if(logfile_io == nil) then return end if(logfile_io:seek() == 0) then logfile_io:write("This is a log of commands sent to this camera from Ruby / AgEagle autopilot\n") logfile_io:write("It is used by uThere Ground Control and other software to synchronize photos with airplane location and orientation.\n") logfile_io:write("This is particularly useful for photostitching applications.\n") logfile_io:write("It is also useful for troubleshooting / diagnosis.\n") logfile_io:write("For more info, contact support@uthere.com\n") logfile_io:write("==============================================\n") end end function logfile_init_master() logfile_filename = "A/DCIM/pictuavRuby_master_log.txt" logfile_master_io = io.open(logfile_filename, "a") -- "a" = Append mode that opens an existing file or a creates a new file for appending. logfile_init_gut(logfile_master_io) end imageDirPrev = "" function logfile_init_imagedir() imageDir = get_image_dir() -- note: this will return a directory name even if it doesn't yet exist if(imageDir == imageDirPrev) then return end imageDirPrev = imageDir -- important to do prior to the following logfile_add to avoid recursion if(logfile_imagedir_io ~= nil) then logfile_imagedir_io:close() logfile_imagedir_io = nil end if((imageDir ~= nil) and (imageDir ~= "")) then os.mkdir(imageDir)-- it may not exist yet logfile_filename = imageDir .. "/pictuavRuby_log.txt" logfile_add("Image directory","Create/append:".."\t"..logfile_filename) logfile_imagedir_io = io.open(logfile_filename, "a") -- "a" = Append mode that opens an existing file or a creates a new file for appending. if(logfile_imagedir_io == nil) then logfile_add("Image directory","***** logfile_imagedir_io is nil") end logfile_init_gut(logfile_imagedir_io) end logfile_add("Image directory",imageDir) end function logfile_add_gut(logfile_io,commandName,value) if(logfile_io == nil) then return end logfile_io:write(os.time()) logfile_io:write("\t") date = os.date("!*t") --If format starts with '!', then the date is formatted in Coordinated Universal Time. "*t" returns an array instead of a string logfile_io:write(string.format("%04i:%02i:%02i %02i:%02i:%02i",date["year"] , date["month"], date["day"] , date["hour"] , date["min"], date["sec"])) logfile_io:write("\t") logfile_io:write(get_tick_count()) logfile_io:write("\t") logfile_io:write(commandName) logfile_io:write("\t") if(value ~= nil) then logfile_io:write(value) end logfile_io:write("\n") logfile_io:flush() end function logfile_add(commandName,value) logfile_init_imagedir() -- ok to call repeatedly. will create a new logfile if the image dir changes logfile_add_gut(logfile_imagedir_io,commandName,value) logfile_add_gut(logfile_master_io,commandName,value) if(value == nil) then value = "" end print(commandName .. " "..value) end function logfile_add_system_values() -- periodically record various values that might be useful in troubleshooting logfile_add("temperature","optical".. "\t" .. get_temperature(0)) logfile_add("temperature","CCD".. "\t" .. get_temperature(1)) logfile_add("temperature","battery".. "\t" .. get_temperature(2)) end -- ------------------------------ MAIN debugging = true print( "uThere Ruby Camera Control" ) print( "http://uthere.com" ) print( "support@uthere.com" ) logfile_init_master() logfile_add("pictuavRuby Version",version) logfile_add("startup","") print( "uThere Ruby Camera Control" ) print( "http://uthere.com" ) print( "support@uthere.com" ) dump_propCase() a = -1; frame = 1; log_sys_time_next = 0; shutter_command_received_last_time_ticks =0; repeat -- http://chdk.wikia.com/wiki/USB_Remote -- USB remote must be enabled for this function to work (from CHDK "Remote Parameters" menu) a = get_usb_power(0) -- returns the pulse width of the most recent USB power activation and clears that value. if(a>0) then logfile_add("usb pulse",a) end if (a>=7) then --retract Ruby is sending 90 msec pulses for retract (does not cause shutdown). sometimes a is as low as 7 lens_retract(); shutter_command_received_last_time_ticks = 0; -- print "Shutting down..." -- shut_down(); elseif(a>=3) then --prefocus Ruby is sending 55 msec pulses for prefocus. When 45 msec pulses were used, it sometimes came in as a=3 prefocus(); shutter_command_received_last_time_ticks = 0; elseif (a>0) then --take a picture Ruby is sending 15 msec pulses for single shutter release. Seeing a=1 and a=2 shutter_command_received_last_time_ticks = get_tick_count() if(cfg_shutter_trigger_rapid_continuous == false) then shutter_trigger() end end if(cfg_shutter_trigger_rapid_continuous == true) then if((shutter_command_received_last_time_ticks ~= 0) and ((get_tick_count() - shutter_command_received_last_time_ticks) < 3000)) then shutter_trigger() end end if(os.time() > log_sys_time_next) then logfile_add_system_values() log_sys_time_next = os.time() + 100 end -- do something like this to yield time? sleep(a*1000 - (get_tick_count() - start)) until ( false ) logfile_add("***END","") -- should never come here