本期活动的成果电子测光表的由来是是从专业测光表的基础功能得来的,通过对专业测光表的功能拆分带到了本次活动及模块,并选取了Adafruit推出的Adafruit ESP32-S3 TFT Feather开发板作为主控,相关模块的基本信息可以参照开箱贴内容:
通过工程师角度实现,对各个模块进行控制,然后实现上述的基本测光功能,整体的思路是先通过各部分的基本模块的控制一步一步实现,再通过整合组合到一起,各部分的控制测试请参照过程贴内容:
在进行各模块了解尺寸接口之后,制作了一个转接板:
用于将各模块进行固定连接的效果如下:
在之前的单一测试中,我们已成功实现了页面展示、按键模块的数据采集,以及至关重要的光强数据采集。接下来,我们将无缝衔接到舵机控制的环节,并在后续的功能讲解中与大家详尽分享。
接下来,让我们深入了解一下我们这次精心打造的电子测光表所具备的具体功能及其设计细节。本次程序设计了两个核心页面,旨在为用户提供流畅且直观的操作体验。首先映入眼帘的是静态显示的欢迎页面,这个页面设计简洁大方,直接显示本次活动的信息。随后,用户只需轻轻一按,即可切换至功能页面,这里是电子测光表真正的舞台。为了确保用户能够直观地获取所需信息,我们在功能页面上精心布局了多个显示区域。光强/ISO/光圈/快门参数等众多参数,其中快门这一输出量,是本次程序设计的精髓所在,它基于光强ISO和光圈参数的精确测量,通过复杂的算法模型,为用户提供了在当前光线环境下最为合适的快门速度建议。这一功能的实现,不仅极大地提升了用户拍摄的便捷性,更在一定程度上保障了拍摄作品的质量。为了让用户能够更直观地感受到快门参数的变化对拍摄效果的影响,我们还特别添加了EA(曝光补偿)数值的动态显示功能。这一功能能够实时反映快门、光圈和ISO三者之间的动态平衡关系,帮助用户更好地理解曝光原理,从而在实际拍摄中更加游刃有余。本次电子测光表的设计不仅功能全面、操作便捷,更在细节之处体现了对用户需求的深刻理解和尊重。这款电子测光表将成为广大摄影爱好者不可或缺的得力助手。
接下来我们看一下关键代码的分析,除了之前单一测试的基础控制代码外,本次加入了舵机的控制代码如下:
import pwmio from adafruit_motor import servo pwm = pwmio.PWMOut(board.D12, frequency=50) motor_servo = servo.ContinuousServo(pwm) motor_servo.throttle = -1
在这里直接使用了电机控制的固件,其调节范围为-1到1,对应的是咱们舵机的转动范围,注意舵机的控制频率为50Hz。相对应的我们增加了一个板载按键的控制,通过这个按键的控制去实现舵机的转动。
在颜色选择方面,我们使用了三种颜色,其中包括基础颜色和基础配置数据颜色和目标数据颜色,分别采用三种不同的颜色进行区分,在显示方面我们通过建立group来进行页面的显示控制不同的group一次只能显示一个,其刷新不需要我们对屏的直接操作,只需要对不同的显示内容进行修改就可以,比如如下我们的功能页面,就是添加了不同的显示内容。
text = label.Label( font=font, # 你需要指定一个字体对象 text="\n EEPW&&\n DigiKey \n Let's do!", # 显示的文本 x=2, # 文本的起始X坐标 y=2, # 文本的起始Y坐标 color=color_FireBrick, # 文本颜色 ) text_Menu1 = label.Label( font=font, # 你需要指定一个字体对象 text="光强:", # 显示的文本 x=5, # 文本的起始X坐标 y=10, # 文本的起始Y坐标 color=color_FireBrick, # 文本颜色 ) text_data1 = label.Label( font=font, # 你需要指定一个字体对象 text=str(lux), # 显示的文本 x=65, # 文本的起始X坐标 y=10, # 文本的起始Y坐标 color=color_WhiteSmoke, # 文本颜色 ) text_Menu2 = label.Label( font=font, # 你需要指定一个字体对象 text="ISO:", # 显示的文本 x=5, # 文本的起始X坐标 y=50, # 文本的起始Y坐标 color=color_FireBrick, # 文本颜色 ) text_data2 = label.Label( font=font, # 你需要指定一个字体对象 text=str(100 * pow(2, IOScnt)), # 显示的文本 x=65, # 文本的起始X坐标 y=50, # 文本的起始Y坐标 color=color_WhiteSmoke, # 文本颜色 ) text_Menu3 = label.Label( font=font, # 你需要指定一个字体对象 text="光圈:", # 显示的文本 x=5, # 文本的起始X坐标 y=100, # 文本的起始Y坐标 color=color_FireBrick, # 文本颜色 ) text_data3 = label.Label( font=font, # 你需要指定一个字体对象 text='F' + AV[AVdata], # 显示的文本 x=65, # 文本的起始X坐标 y=100, # 文本的起始Y坐标 color=color_WhiteSmoke, # 文本颜色 ) text_Menu4 = label.Label( font=font, # 你需要指定一个字体对象 text="快门:", # 显示的文本 x=5, # 文本的起始X坐标 y=150, # 文本的起始Y坐标 color=color_FireBrick, # 文本颜色 ) text_data4 = label.Label( font=font, # 你需要指定一个字体对象 text=str(DVdata), # 显示的文本 x=65, # 文本的起始X坐标 y=150, # 文本的起始Y坐标 color=color_FreshLeaves, # 文本颜色 ) text_Menu5 = label.Label( font=font, # 你需要指定一个字体对象 text="EV :", # 显示的文本 x=5, # 文本的起始X坐标 y=200, # 文本的起始Y坐标 color=color_FireBrick, # 文本颜色 ) text_data5 = label.Label( font=font, # 你需要指定一个字体对象 text=str(DVdata), # 显示的文本 x=65, # 文本的起始X坐标 y=200, # 文本的起始Y坐标 color=color_WhiteSmoke, # 文本颜色 ) # 页面索引 # 页面参数 current_page = 0 # 菜单索引 menu_index = 0 # 启动屏幕显示 show_WELCOME = displayio.Group() show_WELCOME.append(text) show_Menu = displayio.Group() show_Menu.append(text_Menu1) show_Menu.append(text_Menu2) show_Menu.append(text_Menu3) show_Menu.append(text_Menu4) show_Menu.append(text_Menu5) show_Menu.append(text_data1) show_Menu.append(text_data2) show_Menu.append(text_data3) show_Menu.append(text_data4) show_Menu.append(text_data5) def Show_WELCOME(): display.root_group = show_WELCOME def Show_menu(): display.root_group = show_Menu def Allrefresh(): text_data1.text = str(lux) text_data2.text = str(iosdata) text_data3.text = "F"+AV[AVdata] if TVflag: text_data4.text = "1/"+str(TVdata) else: text_data4.text = str(TVdata) text_data5.text = str(EVdata) 刷新不需要我们的直接控制,对应的页面换只需要打开不同的group就可以。 在循环采集过程中,只需要进行对应的逻辑控制就可以,下面是我本次循环显示的一个逻辑控制 while True: time.sleep(0.01) Time_cut = Time_cut + 1 if Time_cut > 100: lux = sensor.lux if lux > 0: EVdata = 2 + math.log(lux/10, 2) + IOScnt TVdata = float(AV[AVdata])*float(AV[AVdata])/pow(2, EVdata) if TVdata >= 1: TVflag = 0 TVdata = math.floor(TVdata) else: TVflag = 1 TVdata = math.floor(1/TVdata) # EVdata = math.floor(EVdata) else: EVdata = 0 TVdata = "--" iosdata = 100 * pow(2, IOScnt) Allrefresh() # print("%.2f Lux" % sensor.lux) Time_cut = 0 if not buttonRed.value: if not buttonRedDown_flag: buttonRedTime_cut = buttonRedTime_cut + 1 if buttonRedTime_cut > 15: buttonRedDown_flag = 1 if current_page: AVdata = AVdata + 1 if AVdata >= 11: AVdata = 0 DVdata = AVdata if buttonRed.value: buttonRedDown_flag = 0 buttonRedTime_cut = 0 if not buttonOK.value: buttonOKTime_cut = buttonOKTime_cut + 1 if buttonOKTime_cut > 30: if buttonOKDown_flag: buttonOKDown_flag = 0 motor_servo.throttle = -1 else: buttonOKDown_flag = 1 motor_servo.throttle = 1 buttonOKTime_cut = 0 if buttonOK.value: buttonOKTime_cut = 0 if not buttonBlue.value: if not buttonBlueDown_flag: buttonBlueTime_cut = buttonBlueTime_cut + 1 if buttonBlueTime_cut > 15: buttonBlueDown_flag = 1 if current_page: IOScnt = IOScnt + 1 if IOScnt >= 7: IOScnt = 0 if buttonBlue.value: buttonBlueDown_flag = 0 buttonBlueTime_cut = 0 if not current_page: if buttonRedDown_flag or buttonBlueDown_flag: Show_menu() current_page = 1 buttonRedDown_flag = 0 buttonRedTime_cut = 0 buttonBlueDown_flag = 0 buttonBlueTime_cut = 0
实现的功能是对iso和光圈的控制,通过按键模块的按键进行切换,分别有不同的档位,而光强是实时采集的,并通过光强计算对应的EA值,然后根据给定的iso和光圈以及采集到的ev值给出快门的推荐范围,使用者可以根据推荐值更接近于哪一方面去自行选择,下图为光强为零时无快门推荐意见的显示:
下图为实际采集光强后的不为零时推荐的相关参数的显示:
这里有几点需要注意?其核心算法是iso/光圈/快门和ev之间的关系,本次实现的功能是通过给定iso可选,给定光圈可选,最后推荐的快门参数。在教程中推荐的log参数:
通过实际的资料查找对应的底为2,而不是10,一定需要注意,下面是光强和ev之间的一个关系,大家计算后可以简单对比一下:
表一左边竖向第一列是整数Ev值,表一横向第一行是Ev值的小数,整数Ev值和Ev值小数交汇的数字就是勒克斯(Lx)。比如11.5 Ev对应的是7240Lx。
下面是本次实现的最终程序附件:
下面是本次的效果展示视频:
【录Let's doDIY活动——电子测光表效果展示】 https://www.bilibili.com/video/BV1chUuYsEUX/?share_source=copy_web&vd_source=f03b02506162c2d03feeb4684b1e6313