Add YASS (Yet Another Sunset Script) v4.8 for CHDK cameras

This commit introduces third-party work by waterwingz, based on scripts
by Fbonomi and soulf2. YASS is an advanced intervalometer script for
CHDK-enabled Canon cameras that automatically adjusts exposure settings
during sunset, sunrise, or other changing light conditions.

Key features:
- Automatic exposure ramping (shutter speed and ISO)
- Intelligent ND filter control
- Focus lock options (AFL/MF at infinity)
- Configurable shot intervals and exposure compensation
- CSV logging of exposure parameters
- Battery-saving display blanking options

Original work released under GPL license.
Requires CHDK 1.2.0 build 3276 or higher (1.3.0+ recommended).

Files added:
- yass/yass4.lua: Main script (v4.8)
- yass/README.md: Documentation and usage guide
This commit is contained in:
yair
2025-11-17 00:26:28 +02:00
parent 340cd1dd6e
commit 30e695f961
2 changed files with 648 additions and 0 deletions

134
yass/README.md Normal file
View File

@@ -0,0 +1,134 @@
# YASS - Yet Another Sunset Script
**Version 4.8** for CHDK-enabled Canon cameras
## Overview
YASS (Yet Another Sunset Script) is an advanced intervalometer script for CHDK that automatically adjusts camera exposure settings during sunset, sunrise, or other changing light conditions. It maintains optimal exposure by intelligently ramping shutter speed and ISO as light levels change.
## Features
- **Automatic Exposure Ramping**: Smoothly adjusts shutter speed and ISO as light changes
- **Intelligent ND Filter Control**: Automatically engages/disengages ND filter based on conditions
- **Focus Lock Options**: AFL (Auto Focus Lock) or MF (Manual Focus) at infinity
- **Configurable Intervals**: Shot rates from 1 second to 1 hour
- **RAW Support**: Optional RAW capture alongside JPEG
- **CSV Logging**: Detailed logging of exposure parameters for each shot
- **Battery Saving**: Display blanking and LED control options
- **Delayed Start**: Optional startup delay up to 4 hours
## Requirements
- Canon camera with CHDK firmware installed
- **Minimum CHDK version**: 1.2.0 build 3276 or higher
- **Recommended**: CHDK 1.3.0 build 3383 or higher
- Sufficient SD card space (configurable shutdown threshold)
## Installation
1. Copy [`yass4.lua`](yass/yass4.lua) to your CHDK scripts folder (typically `CHDK/SCRIPTS/`)
2. Load the script from the CHDK menu
3. Configure parameters as needed
4. Start the script
## Parameters
### Timing & Interval
- **Shot Rate (t)**: Interval between shots (1-3600 seconds, default: 10s)
- **First Shot Delay (m)**: Startup delay (0-240 minutes, default: 0)
### Exposure Control
- **Compensation (o)**: Exposure compensation (-4.0 to +4.0 stops, default: 0.0)
- **Tv normal limit (e)**: Maximum shutter speed threshold (default: 1/6s)
- **Tv low limit (b)**: Minimum shutter speed (1.0-32 seconds, default: 5.0s)
- **Exposure ramp rate (f)**: Speed of exposure changes (VSlow/Slow/Medium/Fast/VFast)
### ISO Settings
- **ISO default (c)**: Starting ISO value (80-1600, default: 100)
- **ISO upper limit (d)**: Maximum ISO (80-1600, default: 400)
### Aperture & ND Filter
- **Aperture Setting (a)**: F-stop to use (min_Av to f16, default: min_Av)
- **Use ND Filter (w)**: Enable automatic ND filter control (0=off, 1=on)
### Focus & Zoom
- **Focus @ Infinity Mode (x)**: None/AFL/MF (default: None)
- **Zoom step position (z)**: Zoom position (-1=current, 0-100)
### File & Display Options
- **RAW enable (r)**: Capture RAW files (0=off, 1=on)
- **Free Disk MB Shut down (h)**: Stop when disk space below threshold (10-100 MB)
- **Status LED (y)**: LED indicator mode (Off/0-8)
- **Display blanking mode (n)**: None/BKLite/DispKey/ShrtCut
- **Logging (l)**: Off/Screen/SDCard/Both
## How It Works
1. **Initialization**: Script sets up camera (disables flash, IS, AF assist)
2. **Exposure Calculation**: Reads current brightness and calculates needed exposure
3. **Ramping Logic**:
- When light is bright: Uses fast shutter speeds at default ISO
- As light dims: Gradually increases shutter speed
- When shutter reaches low limit: Begins increasing ISO
- ND filter engages automatically for very bright conditions
4. **Continuous Shooting**: Captures images at specified interval
5. **Logging**: Records exposure data to `A/yass4.csv` (if enabled)
## Usage Tips
### Sunset/Sunrise Time-lapse
```
Shot Rate: 10-30 seconds
Tv low limit: 5-15 seconds
ISO upper limit: 800-1600
Exposure ramp rate: Medium or Slow
Focus mode: MF (Manual Focus at infinity)
```
### Long Duration Sessions
```
Display blanking mode: BKLite or ShrtCut (saves battery)
Status LED: Off or low setting
Logging: SDCard (for analysis)
First Shot Delay: Set to start automatically
```
### Bright Conditions
```
Use ND Filter: On
Aperture: f5.6 or smaller
ISO default: 80 or 100
```
## Log File Format
The script creates `A/yass4.csv` with the following columns:
```
Shot, Image, Compensation, Tv_target, Tv, Tv+, ND, ISO_target, ISO, f-stop, focus, Tv96, Sv96, Av96, Bv96
```
## Credits
- **Based on**: Scripts by Fbonomi and soulf2
- **Author**: waterwingz
- **License**: GPL (GNU General Public License)
## Troubleshooting
- **Script won't start**: Check CHDK version meets minimum requirements
- **Exposure issues**: Adjust compensation parameter and ramp rate
- **Focus problems**: Try different focus modes (AFL vs MF)
- **Battery drain**: Enable display blanking and reduce LED usage
- **Disk full**: Increase shutdown threshold or use larger SD card
## Version History
**v4.8** (Current)
- Enhanced CHDK 1.3.0 compatibility
- Improved exposure ramping algorithm
- Better ND filter control
- Extended logging capabilities
---
*For more information about CHDK, visit the [CHDK Wiki](https://chdk.fandom.com/)*

514
yass/yass4.lua Normal file
View File

@@ -0,0 +1,514 @@
--[[
Yet Another Sunset Script v4.8
@title yass 4.8
@chdk_version 1.3
-- Based on scripts by Fbonomi and soulf2
-- Released under GPL by waterwingz
@param t Shot Rate (sec)
@default t 10
@range t 1 3600
@param o Compensation (f-stops)
@default o 8
@values o -4.0 -3.5 -3.0 -2.5 -2.0 -1.5 -1.0 -0.5 0.0 0.5 1.0 1.5 2.0 2.5 3.0 3.5 4.0
@param e Tv normal limit (sec)
@default e 9
@values e Off 2.0 1.6 1.3 1.0 0.8 0.6 0.5 0.4 0.3 1/4 1/5 1/6 1/8 1/10 1/13 1/15
@param b Tv low limit (sec)
@default b 5
@values b 32 25 20 16 12 10 8.0 6.0 5.0 4.0 3.2 2.5 2.0 1.6 1.3 1.0
@param c ISO default
@default c 1
@values c 80 100 125 160 200 250 320 400 500 640 800 1250 1600
@param d ISO upper limit
@default d 7
@values d 80 100 125 160 200 250 320 400 500 640 800 1250 1600
@param f Exposure ramp rate
@default f 2
@values f VSlow Slow Medium Fast VFast
@param a Aperture Setting
@default a 0
@values a min_Av f1.8 f2.0 f2.8 f3.2 f4.0 f5.6 f6.3 f8 f11 f16
@param m First Shot Delay (min)
@default m 0
@range m 0 240
@param z Zoom step position
@default z -1
@range z -1 100
@param w Use ND Filter?
@default w 1
@range w 0 1
@param x Focus @ Infinity Mode
@default x 0
@values x None AFL MF
@param r RAW enable?
@default r 0
@range r 0 1
@param h Free Disk MB Shut down
@default h 15
@range h 10 100
@param y Status LED
@default y 2
@values y Off 0 1 2 3 4 5 6 7 8
@param n Display blanking mode
@default n 0
@values n None BKLite DispKey ShrtCut
@param l Logging
@default l 2
@values l Off Screen SDCard Both
--]]
props=require("propcase")
tv_ref = { -- note : tv_ref values set 1/2 way between shutter speed values
-608, -560, -528, -496, -464, -432, -400, -368, -336, -304,
-272, -240, -208, -176, -144, -112, -80, -48, -16, 16,
48, 80, 112, 144, 176, 208, 240, 272, 304, 336,
368, 400, 432, 464, 496, 528, 560, 592, 624, 656,
688, 720, 752, 784, 816, 848, 880, 912, 944, 976,
1008, 1040, 1072, 1096, 1129, 1169, 1192, 1225, 1265, 1376 }
av_ref = {176,208,240,272,304,336,368,400,432,464,496,512,528,560,592,624,656,688,720,752}
sv_ref = {378,420,440,462,486,531,576,612,645,678,709,739,772,805}
-- translate user parameter into usable values & names
interval = t*1000
if (e >0) then tv_normal_limit = tv_ref[e+15] else tv_normal_limit = 1192 end
tv96_low_limit = tv_ref[b+4]
apex96_ramp_rate =(f+1) * math.min(1+(t/8),10)
sv96_default = sv_ref[c+1]
sv96_max = sv_ref[d+1]
av_table = {0, 171,192,288,320,384,480,512,576,672,768}
av96 = av_table[a+1]
zoom_setpoint = z
focus_mode = x
min_disk_space = h*1024
compensation = (o-8)*48
nd_filter = w
log_mode = l
display_mode = n
status_led = y-1
-- constants
nd96=288 -- ND filter's number of equivalent f-stops
nd_tv_limit = 960 -- engage ND filter if tv < 1/1000 sec
SHORTCUT="print" -- edit this if using shortcut key to enter sleep mode
-- camera configuration
shot=1
done=0
timestamp=0
led_state = 0
led_timer = 10
display_state = 1
display_hold_timer = 0
function restore()
set_backlight(1)
set_led(status_led,0)
set_raw(original_raw)
unlock_focus()
end
function update_zoom()
local count = 0
zsteps=get_zoom_steps()
if(zoom_setpoint>zsteps) then zoom_setpoint=zsteps end
print("setting zoom to step",zoom_setpoint,"of",zsteps)
sleep(2000)
set_zoom(zoom_setpoint)
sleep(2000)
press("shoot_half")
repeat
sleep(100)
count = count + 1
until (get_shooting() == true ) or (count > 40 )
release("shoot_half")
end
-- focus lock and unlock
function lock_focus()
if (focus_mode > 0) then -- focus mode requested ?
if ( focus_mode == 1 ) then -- method 1 : set_aflock() command enables MF
if (chdk_version==120) then
set_aflock(1)
set_prop(props.AF_LOCK,1)
else
set_aflock(1)
end
if (get_prop(props.AF_LOCK) == 1) then print(" AFL enabled") else print(" AFL failed ***") end
else -- mf mode requested
if (chdk_version==120) then -- CHDK 1.2.0 : call event proc or levents to try and enable MF mode
if call_event_proc("SS.Create") ~= -1 then
if call_event_proc("SS.MFOn") == -1 then
call_event_proc("PT_MFOn")
end
elseif call_event_proc("RegisterShootSeqEvent") ~= -1 then
if call_event_proc("PT_MFOn") == -1 then
call_event_proc("MFOn")
end
end
if (get_prop(props.FOCUS_MODE) == 0 ) then -- MF not set - try levent PressSw1AndMF
post_levent_for_npt("PressSw1AndMF")
sleep(500)
end
elseif (chdk_version >= 130) then -- CHDK 1.3.0 : set_mf()
if ( set_mf(1) == 0 ) then set_aflock(1) end -- as a fall back, try setting AFL is set_mf fails
end
if (get_prop(props.FOCUS_MODE) == 1) then print(" MF enabled") else print(" MF enable failed ***") end
end
sleep(1000)
if(set_focus(50000)==0) then print("infinity focus error") end
sleep(1000)
end
end
function unlock_focus()
if (focus_mode > 0) then -- focus mode requested ?
if (focus_mode == 1 ) then -- method 1 : AFL
if (chdk_version==120) then
set_aflock(0)
set_prop(props.AF_LOCK,0)
else
set_aflock(0)
end
if (get_prop(props.AF_LOCK) == 0) then print("AFL unlocked") else print("AFL unlock failed") end
else -- mf mode requested
if (chdk_version==120) then -- CHDK 1.2.0 : call event proc or levents to try and enable MF mode
if call_event_proc("SS.Create") ~= -1 then
if call_event_proc("SS.MFOff") == -1 then
call_event_proc("PT_MFOff")
end
elseif call_event_proc("RegisterShootSeqEvent") ~= -1 then
if call_event_proc("PT_MFOff") == -1 then
call_event_proc("MFOff")
end
end
if (get_prop(props.FOCUS_MODE) == 1 ) then -- MF not reset - try levent PressSw1AndMF
post_levent_for_npt("PressSw1AndMF")
sleep(500)
end
elseif (chdk_version >= 130) then -- CHDK 1.3.0 : set_mf()
if ( set_mf(0) == 0 ) then set_aflock(0) end -- fall back so reset AFL is set_mf fails
end
if (get_prop(props.FOCUS_MODE) == 0) then print("MF disabled") else print("MF disable failed") end
end
sleep(100)
end
end
function print_tv(val)
local i = 1
local tv_str = {
">64",
"64", "50", "40", "32", "25", "20", "16", "12", "10", "8.0",
"6.0", "5.0", "4.0", "3.2", "2.5", "2.0", "1.6", "1.3", "1.0", "0.8",
"0.6", "0.5", "0.4", "0.3", "1/4", "1/5", "1/6", "1/8", "1/10", "1/13",
"1/15", "1/20", "1/25", "1/30", "1/40", "1/50", "1/60", "1/80", "1/100", "1/125",
"1/160", "1/200", "1/250","1/320","1/400","1/500","1/640","1/800","1/1000","1/1250",
"1/1600","1/2000","1/2500","1/3200","1/4000","1/5000","1/6400","1/8000","1/10000","hi" }
while (i <= #tv_ref) and (val > tv_ref[i]-1) do i=i+1 end
return tv_str[i]
end
function print_av(val)
local i = 1
local av_str = {"n/a","2.0","2.2","2.5","2.8","3.2","3.5","4.0","4.5","5.0",
"5.6","5.9","6.3","7.1","8.0","9.0","10.0","11.0","13.0","14.0","16.0"}
while (i <= #av_ref) and (val > av_ref[i]-1) do i=i+1 end
return av_str[i]
end
function print_sv(val)
local i = 1
local sv_str = {"n/a","80","100","125","160","200","250","320","400","500","640","800","1250","1600","3200"}
while (i <= #sv_ref) and (val > sv_ref[i]-1) do i=i+1 end
return sv_str[i]
end
function sd_log(logmsg)
if ( log_mode > 1 ) then
local logname="A/yass4.csv"
retry = 0
repeat
log=io.open(logname,"a")
if ( log~=nil) then
log:write(os.date(s,tm),": ",logmsg,"\n")
log:close()
return
end
sleep(250)
retry = retry+1
until(retry > 7)
print("Error : log file open fault!")
end
end
function lprint(logmsg)
if ( log_mode > 0) then
if (( log_mode == 1) or (log_mode == 3)) then print(logmsg) end
sd_log(logmsg)
end
end
function sleep_mode(m) -- press user shortcut key to toggle sleep mode
press(SHORTCUT)
sleep(1000)
release(SHORTCUT)
sleep(500)
end
function set_display_key(m) -- click display key to get to desire LCD display mode 0=off, 1=on
if (m==0) then m=2 else m=0 end
local count=5
local clicks=0
sleep(200)
repeat
disp = get_prop(props.DISPLAY_MODE)
if ( disp ~= m ) then
click("display")
clicks=clicks+1
sleep(500)
end
count=count-1
until (( disp==m ) or (count==0))
if (clicks>0 and count==0) then
print("unable to change display")
end
end
function set_display(m) -- m=0 for turn off display, m>0 turn on for m seconds
if (display_mode>0) then
if ( display_hold_timer>0) then
if (m>1) then display_hold_timer = display_hold_timer+m end
else
if (m>1) then
display_hold_timer = m
m=1
end
local st="off"
if (m>0) then st="on" end
if ( display_mode==1) then
if (display_state ~= m) then print("set backlight "..st) end
sleep(1000)
set_backlight(m)
elseif ( display_mode==2) then
if( display_state ~= m) then
print("set display "..st)
set_display_key(m)
if ( focus_mode > 0 ) then
lock_focus()
end
end
elseif ( display_mode==3) then
if (display_state ~= m) then
print("toggle sleep mode")
sleep_mode(m)
end
end
display_state=m
end
end
end
--[[ ========================== Main Program ================================= --]]
set_console_layout(2, 0, 44, 10)
print("Yet Another Sunset Script V4.8")
bi=get_buildinfo()
chdk_version= tonumber(string.sub(bi.build_number,1,1))*100 + tonumber(string.sub(bi.build_number,3,3))*10 + tonumber(string.sub(bi.build_number,5,5))
if ( tonumber(bi.build_revision) > 0 ) then
build = tonumber(bi.build_revision)
else
build = tonumber(string.match(bi.build_number,'-(%d+)$'))
end
if (chdk_version<120) then
printf("CHDK 1.2.0 or higher required")
elseif((chdk_version==120)and(build<3276)) then
printf("CHDK 1.2.0 build 3276 or higher required")
elseif((chdk_version==130)and(build<3383)) then
printf("CHDK 1.3.0 build 3383 or higher required")
else
print("CHDK version okay")
lprint(" tvl="..print_tv(tv96_low_limit).." tvh="..print_tv(tv_normal_limit).." ISOd="..print_sv(sv96_default).." ISOm="..print_sv(sv96_max))
lprint(" shot rate="..(interval/1000).." ramp:"..apex96_ramp_rate)
sleep(200)
-- setup - disable flash, image stabilization and AF assist lamp
original_raw = get_raw()
set_raw(r)
set_prop(props.FLASH_MODE, 2) -- flash off
set_prop(props.IS_MODE, 3) -- IS_MODE off
set_prop(props.AF_ASSIST_BEAM,0) -- AF assist off if supported for this camera
-- startup delay ?
if ( m>0 ) then
lprint("delaying startup for",m,"minutes")
sleep(10)
set_display(0)
sleep(m*60000)
end
-- switch to shooting mode if necessary
if ( get_mode() == false ) then
set_record(1)
while ( get_mode() == false ) do sleep(100) end
end
-- set zoom position
if ( zoom_setpoint > -1 ) then update_zoom() end
-- lock focus at infinity ?
lock_focus()
-- set initial shooting values (values are with ND filter out)
press("shoot_half")
repeat sleep(50) until get_shooting() == true
release("shoot_half")
repeat sleep(50) until get_shooting() == false
sleep(100)
dof=get_dofinfo()
if ((dof.focus == -1) or (dof.focus>20000)) then
print(" focus is at infinity")
elseif ( dof.focus>dof.hyp_dist ) then
print(" focus is past hyperfocal")
else
print(" focus is at "..dof.focus.." mm")
end
if (av96 == 0) then -- are we in min_AV mode ?
av96 = get_prop(props.MIN_AV)
end
sv96 = sv96_default
-- put titles into log file
sd_log("YASS 4.3 "..os.date(s,tm))
sd_log("Shot,Image,Compensation,Tv_target,Tv,Tv+,ND,ISO_target,ISO,f-stop,focus,Tv96,Sv96,Av96,Bv96")
sleep(2000)
-- initialize timers
ticsec = 0
ticmin = 0
set_display(20)
set_console_layout(2, 0, 44, 3)
print("started")
-- loop here until a key is pressed - exit if its the Menu key, else enable the display for 20 seconds
repeat
repeat
now = get_tick_count()
if ( now > timestamp + interval ) then
timestamp=get_tick_count()
if ( interval > 5000 ) then -- check if in slow mode
press("shoot_half") -- get current exposure readings
repeat sleep(50) until get_shooting() == true
release("shoot_half")
repeat sleep(50) until get_shooting() == false
end
bv96=get_bv96()
bv96c=bv96-compensation -- calulate compensated exposure needed
tv96needed=sv96_default+bv96c-av96 -- calculate desired shutter speed at default ISO
if (tv96==nil) then tv96=tv96needed end
if ( compensation >= 0 ) then
compstr = "+"..compensation
else
compstr = "-"..compensation
end
if(tv96needed>tv_normal_limit) then
tv96=(tv96+tv96needed)/2 -- use that shutter speed if above Tv normal limit
sv96=sv96_default -- use default ISO
sv96needed=sv96_default --
else
if( tv96 > tv96needed) then
tv96=math.max(tv96-apex96_ramp_rate,tv96needed) -- lower the shutter speed at the APEX ramp rate
else
tv96=math.min(tv96+apex96_ramp_rate,tv96needed) -- increase the shutter speed at the APEX ramp rate
end
if (tv96>tv96_low_limit) then
sv96=sv96_default -- use default ISO value while Tv above lower limit
sv96needed=sv96_default --
else
tv96=tv96_low_limit -- if Tv at low limit then recalculate ISO value
sv96needed=tv96+av96-bv96c
if (sv96needed>sv96_max) then
sv96needed=sv96_max -- clamp ISO at high limit
end
if( sv96 > sv96needed) then
sv96=math.max(sv96-apex96_ramp_rate,sv96needed) -- lower the ISO at the APEX ramp rate
else
sv96=math.min(sv96+apex96_ramp_rate,sv96needed) -- increase the ISO at the APEX ramp rate
end
end
end
if (nd_filter==1) then
if (tv96 > nd_tv_limit ) then
tv96m = tv96-nd96
set_nd_filter(1) -- activate the ND filter
nd_string=" ["..print_tv(tv96m).."+NDin] "
nd_state=1
else
tv96m = tv96
set_nd_filter(2) -- make sure the ND filter does not activate
nd_string=" [NDout] "
nd_state=0
end
else
tv96m = tv96
set_nd_filter(2) -- make sure the nd filter does not activate
nd_string=" "
nd_state=0
end
set_sv96(sv96)
set_av96_direct(av96)
set_tv96_direct(tv96m)
shoot()
if ( interval > 5000 ) then -- check if in slow mode
sleep(500)
local dof=get_dofinfo()
local imgname=string.format('IMG_%04d.JPG',get_exp_count())
sd_log(shot..","..imgname..","..compstr..","..print_tv(tv96needed)..","..print_tv(tv96)..","..print_tv(tv96m)..","..nd_state..","
.. print_sv(sv96needed)..","..print_sv(sv96)..","..print_av(av96)..","..dof.focus..","..get_tv96()..","..get_sv96()..","..get_av96()..","..get_bv96()..",")
end
print(shot.." Bv:"..bv96..compstr.." Tv*:"..print_tv(tv96needed).." Tv:"..print_tv(tv96)..nd_string.."ISO"..print_sv(sv96))
shot=shot+1
if ( display_mode ~=2 ) then display_state = 1 end -- shooting re-enables backlight and exits sleep mode (if used)
set_display(0)
end
-- process things that happen once every 15 seconds
if ( now > ticmin ) then
ticmin = now+15000
set_display(0)
collectgarbage()
end
-- process things that happen once per second
if ( now > ticsec ) then
ticsec = now+1000
if( display_hold_timer>0) then display_hold_timer=display_hold_timer-1 end
end
-- status LED
led_timer = led_timer -1
if (led_timer == 1) then
set_led(status_led,1)
elseif (led_timer == 0) then
set_led(status_led,0)
led_timer = 20
end
-- check for user input
wait_click(100)
until not( is_key("no_key"))
print("key pressed")
set_display(20)
until ( is_key("menu") or ( get_free_disk_space() < min_disk_space))
if ( get_free_disk_space() < min_disk_space) then
print("SD card full")
else
print("menu key exit")
end
restore()
end