From 30e695f961a82728a225ff228eb2a603a513f0e2 Mon Sep 17 00:00:00 2001 From: yair Date: Mon, 17 Nov 2025 00:26:28 +0200 Subject: [PATCH] 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 --- yass/README.md | 134 +++++++++++++ yass/yass4.lua | 514 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 648 insertions(+) create mode 100644 yass/README.md create mode 100644 yass/yass4.lua diff --git a/yass/README.md b/yass/README.md new file mode 100644 index 0000000..959fbfc --- /dev/null +++ b/yass/README.md @@ -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/)* \ No newline at end of file diff --git a/yass/yass4.lua b/yass/yass4.lua new file mode 100644 index 0000000..1f6a6d1 --- /dev/null +++ b/yass/yass4.lua @@ -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