本次电子测光表项目,我选择了进阶任务,即需要实现基础的测光表功能,也需要通过蓝牙把测光结果上报给手机,且手机也能远程控制舵机按压快门。
在过程贴中已分别实现了基础任务和进阶任务,这里就不赘述单个任务实现,下方是各个帖子链接:
1. 开箱贴:https://forum.eepw.com.cn/thread/387450/1
2. 基础任务-按键检测: https://forum.eepw.com.cn/thread/387473/1
3. 基础任务-编程获取BH1750光强度数值:https://forum.eepw.com.cn/thread/387486/1
4. 基础任务-屏幕显示文字:https://forum.eepw.com.cn/thread/387772/1
5. 基础任务-屏幕显示光强:https://forum.eepw.com.cn/thread/387780/1
6. 进阶任务-蓝牙传输与舵机控制:https://forum.eepw.com.cn/thread/387985/1
项目硬件
此次项目我采用了如下器件:
(1)控制器是 ESP32-S3 评估板-5483,即 Adafruit ESP32S3 TFT Feather 模块,它是一款基于 ESP32-S3芯片的嵌入式开发模块,配备了一块1.14寸彩色IPS TFT显示屏,采用 ST7789 芯片组。该模块通过SPI接口与 ESP32S3 连接,支持DMA,用于提高渲染性能。此模块的应用方向:智能家居、工业自动化、网联网应用。
(2)数字光传感器是 BH1750 U136,它是一款由日本罗姆半导体生产的数字环境光强度传感器,能够测量周围环境的光照强度,并将其转换为数字信号输出,测量范围 0~65535lux,最小误差变动±20%,使用I2C总线进行通信,易于微控制器或者其他支持I2C的设备进行集成。该传感器广泛应用于智能家居、安防系统、户外照明控制等。
(3)按钮模块 U025,是一款由M5 Stack 设计的按键模块,由2个不同颜色的按键组成,支持 Grove 接口,广泛应用于游戏设备、自动化测试、教育机器人、智能家居控制系统。
(4)舵机 SER0043,是一款能够实现360度连续旋转的微型舵机,内部采用塑料齿轮传动,能够在 4.8~6V 电源下工作,提供 1.2~1.6Kg*cm的扭矩。应用于机器人和自动化,模型制作,监控系统。
系统框图
1. 中间的是主控 Adafruit ESP32S3 TFT Feather 模块,板载一个IPS TFT 显示屏,能展示欢迎界面、测光信息;板载一个复位按键和一个用户按键,用户按键作为确认按键,用来调整曝光三要素的值;
2. 图中左侧按键是外接的按钮模块U025,一个红色和一个蓝色按键通过数字IO口接入到主控模块,通过高低电平检测按键状态;这两个按键用来在GUI上导航;
3. 图中上方的数字光传感器通过I2C接口与主控通信,连续采集光强度得到,实时显示在GUI上;
4. 图中右侧是360度舵机,主控通过PWM信号控制舵机旋转;这里舵机没有接负载,旋转一定角度再回归原位模拟按压快门;
5. 下方是移动设备,通过蓝牙BLE与主控通信;主控把采集到的光照强度通过BLE发送给移动设备,也能接收移动设备发送过来的命令控制舵机旋转;
电路原理图
Adafruit ESP32S3 管脚图如上,下方原理图示意图仅表示各个模块与主控的连接关系,不代表实际走线。
1. BOOT 按键是板载按键,连接到GPIO0
2. 舵机连接到GPIO16 【特别注意,我购买的舵机是5V供电,开发板只有3V3电源,因此我外接了一个5V电源,最后GND与开发板相连】
3. 双按键模块,红色按键接到GPIO12, 蓝色按键接到GPIO11
4. BH1750 数字光传感器是I2C接口,SCL是GPIO11,SDA是GPIO22
5. 各个模块的电源和GND连接到对应的管脚。
实物连接
如何开启运行
我设计了两个UI界面,第一个是欢迎界面,展示此次活动信息、作者信息;第二个是测光界面,用户可以通过红蓝按键进行导航,按下BOOT键修改选项,调整曝光三要素中的任意两个参数。
上电之后进入欢迎界面此页面展示活动信息,右上角有一个小的按键,按下 BOOT 键可以跳转到测光界面。
测光界面
此页面可以调整测光模式(AV表示光圈优先,TV表示快门优先)、ISO、AP(光圈档位)、SP(快门速度档位),最下方展示光强度数值 xx Lux 以及 ISO 为100 时的曝光值。
当前图片是Gui Guider 设计器的截图,各个档位和数值仅作为演示,不代表测光结果中的一个可能。
1. 左上角按钮图标,按下可以跳转到欢迎界面;
2. 第一行 Mode: AV 表示当前是 AV 光圈优先模式。当导航到此按钮图标时,按下 BOOT 键可以切换测光模式,在 AV/TV 之间进行切换;当处于AV测光模式时,下方的 SP 档位选项按钮图标变成灰色的,用户不能通过红蓝按键导航到 SP 档位修改按钮图标上;当处于 TV 测光模式时,下方的 AP 档位选项按钮图标变成灰色的,用户不能通过红蓝按键导航到SP档位修改按钮图标上;
3. 第二行 ISO 档位,用户通过红蓝按键导航到ISO时,可以加减ISO档位;
4. 第三行AP 档位,用户通过红蓝按键导航到AP时,可以加减AP档位;
5. 第四行 SP档位,用户可通过红蓝按键导航到SP时,可以加减 SP 档位;
6. 第五行的 LUX 和 EV 是标签,仅展示采集的光强度 lux 数值和 ISO 为100时对应的曝光数值,用户不可修改这两个标签页的数值;
曝光三要素
术语:曝光值(EV)、光圈(AV)、快门(TV)、感光度(SV)、照度(Lux)
计算关系如下
了解了这个公式之后,就能理解下标中的 AV 档位0表示光圈数值1,档位1表示光圈数值1.4,依次类推;快门值 TV 档位 0 表示曝光时间 1,档位 1 表示曝光时间 1/2 秒,依次类推。
在有 EV = 2 + log2(LUX/100) 之后,只需要修改 AV / TV / SV 三者中的任何一个,就可以推算出另外一个。当前我们只设计曝光优先(可调节AV, SV 推导出TV)、快门优先模式(可调节 TV, SV 推导出 AV)。
代码实现
基于 Arduino 开发环境,安装各个模块的库文件,可以轻松驱动各个模块,组合实现产品应用。
1. 驱动按键,安装了 thomasfredericks/Bounce2@^2.72 库
2. 驱动BH1750,安装了 claws/BH1750@^1.3.0 库
3. 驱动舵机,安装了 madhephaestus/ESP32Servo@^3.0.5
4. 为了屏幕显示,安装了 lvgl/lvgl@8.3.10 , adafruit/Adafruit ST7735 and ST7789 Library@^1.10.4 及其依赖的库
初始化初始化函数 setup() 仅调用各个模块的初始化函数,解耦代码,干净清爽。
各个模块的代码都放在各个模块的文件中,例如 button 模块放在 btn_drv.cpp 文件中,其中初始化 button 的代码如下:
由于按键接入了 LVGL 输入设备,模拟旋转编码器,并没有死循环函数。下面是个单个按键的扫描函数:
死循环主 loop()
死循环也是调用各个模块的循环函数,需要注意各个模块的循环函数不要阻塞,否则其他任务得不到运行。
简单介绍:
1. loop_bh1750_scan() 得到测光数值,
2. loop_ble_server_task() 更新BLE状态,检测有无客户端连接,处理连接状态;
3. loop_servo_task() 处理BLE接收到的命令,执行舵机转动命令;
4. 重点在 loop_ui_task(),如下所示:
在实际运行时,宏定义 GUI_APP_LIGHT_METER_ENABLE 定义为1,需要注意,不是每次进入这个循环都需要通知 LVGL 更新 lux 数值显示,这里用 millis() 计算两次进来的时间,当时间超过300毫秒才给 LVGL 的一个控件 guider_ui.screen_lightmeter_lbl_lux_value发送 LV_EVENT_REFRESH 事件, LVGL 接收到事件去调用回调函数处理事件。
loop_ui_task
LVGL 中处理处理这个事件的回调函数如下
l 首先调用 bh1750_get_lux() 获取全局变量(缓存光照强度数值)得到 lux;
l 然后更新在 UI 控件上,然后调用 calc_ev_by_lux(lux) 得到 ISO = 100 时的曝光值,并且显示在 UI 控件上;
l 最后调用 custom_meter_update_widgets() 函数,更新 AV/TV/SV 三个设置,并显示在 GUI 上。
函数 custom_meter_update_widgets() 实现
此函数也不复杂,根据当前选择的测光模式调用不同的计算曝光三要素的函数:
1. 如果当前测光模式是 AV ,则根据 ev0, iso, av 档位计算得到 sp_idx 快门速度档位,然后更新对应的 GUI 控件,显示计算出来的快门速度;
2. 如果当前测光模式是 TV,则根据 ev0, iso, sp 档位计算得到 av_idx 光圈档位,然后更新对应的 GUI 控件,显示计算出来的光圈大小;
此函数根据 EV, ISO 和 AV 计算得到 SP 快门速度档位。
计算公式如下:
根据 EV,ISO 和快门速度T 计算出光圈大小。
推导出的公式如下:
难点 -- LVGL移植输入驱动我移植了 LVGL 显示驱动,显示正常,可以运行 lvgl 示例工程,还需要加入按键,与UI交互以调节曝光三要素中的某些参数。以前都使用触摸屏,但这个开发板的屏幕不支持触摸,只能考虑用按键与UI交互。
LVGL 移植输入驱动,花了老大精力,一开始把按键当做 LV_INDEV_TYPE_KEYPAD 输入类型进行移植,奈何水平有限,折腾了几天没搞定;后来把按键当做 LV_INDEV_TYPE_BUTTON 设备,一个按键对应一个屏幕上的像素点,我尽量精简 GUI 控件,奈何也需要至少8个按键,接入了扩展板添加按键,扩展板上以前验过的按键居然出问题了,一直都是低电平导致按键没有释放,换了其他IO口还是没有解决,作废;最后网上找教程,偶然发现3个按键就可以模拟旋转编码器,尝试一下,居然成功了,基于 LVGL 的 GUI 应用终于把底层打通了。
使用三个按键就模拟编码器的核心代码如下,显示 lv_port_indev_init() 注册 LV_INDEV_TYPE_ENCODER 类型的输入设备。
其次是函数 encoder_read() 实现按键模拟旋转编码器:
总结
1. Arduino 环境开发便捷,各种库都有,方便快速开发应用,但是不方便调试,代码出问题只能靠打日志来追查,此外编译、下载速度真的很感人,使用 PlatformIO IDE 默认增量编译,哪怕改了一行代码,从开始编译、下载完成至少1分钟,极大影响编程状态;
2. ESP32 在 Arduino 环境下的蓝牙开发真的很爽,安装 h2zero/NimBLE-Arduino@^1.4.2 库,参考例程,几分钟就能写出一个简单的 BLE 应用;
3. LVGL 移植显示驱动,非常简单,因为 Adafruit ESP32S3 TFT Feather 已经有了显示驱动,只需要在LVGL刷新缓冲区到显示屏时调用 Adafruit ST77xx 的 API 即可;
4. LVGL UI 设计工具采用 NXP 的 Gui Guider 工具,功能齐全,快速布局,导出代码很方便集成到嵌入式开发环境;
5. 此次活动加深了摄影基础知识-曝光三要素的关系,弄懂了 EV(曝光值) = AV (光圈) + TV (快门) - SV (感光度) 这个公式;
视频演示
见B站视频:
https://www.bilibili.com/video/BV1q2iZYWEhG/?vd_source=8f2bbf56b70c541bec2ea0b9f102ebee
代码见gitee:
https://gitee.com/LinTeX9527/eepw_s3_diy_lintex9527