电子测光表的成果展示视频见B站:https://www.bilibili.com/video/BV144zdYgEeK?t=150.1
EETV视频链接:https://v.eepw.com.cn/video/play/id/16189

-----------------------------------------------------------------------------------------------------------------------------
视频的前4分钟是基础功能,通过按键可设置电子测光表的曝光时间(Exposure Time)、光圈值(Aperture Value)以及感光度(ISO, sensitivity), 通过这三个数据,程序可计算住曝光值(Exposure Valuie), 光照度传感器采集到的环境光照度值(Illuminance)。 这5个数据都显示在LCD显示屏上,如下图所示。

当环境光照度值大于所设置的曝光照度值时,舵机会旋转,带动压杆压着蓝色按钮,蓝色按钮被按下,微处理器板上的LCD灯亮,模拟了照相快门按下的功能。
---------------------------------------------------------------------------------------------------------------------------------
视频的后半部分(第4分钟后)展示的是网络控制和显示功能。
在网络端用的是io.adafruit.com网站提供的网页开发环境,利用该开发环境可以通过拖拽的方法进行简单的图形界面的编程,实现与边缘端设备的通信。
测光表上显示的光照度值每隔10秒钟通过MQTT协议发送一次,传输到网络上,手机接受网络上的数据,同步的显示在屏幕上。

手机端的三个滑杆可以设置曝光时间(Exposure Time)、光圈值(Aperture Value)以及感光度(ISO, sensitivity),并将这些数据发送到测光表上。
在测光表上计算出来的曝光照度值,也能够显示在手机屏幕上

在电脑端也可以控制电子测光表:

------------------------------------------------------------------------------------------------------------------------------
code.py的代码:
import time
import ssl
import os
from random import randint
import microcontroller
import socketpool
import wifi
import board
import neopixel
import adafruit_minimqtt.adafruit_minimqtt as MQTT
from adafruit_io.adafruit_io import IO_MQTT
import displayio
import terminalio
from adafruit_display_text.text_box import TextBox
import adafruit_bh1750
from digitalio import DigitalInOut, Direction, Pull
import pwmio
from adafruit_motor import servo
# create a PWMOut object on the control pin.
pwm = pwmio.PWMOut(board.D5, duty_cycle=0, frequency=50)
servo = servo.Servo(pwm)
# aperture value
AV_value = (1, 1.4, 2, 2.8, 4, 5.6, 8, 11, 16, 22, 32)
#exposure time
TV_value = (1, 1/2, 1/4, 1/8, 1/15, 1/30, 1/60, 1/125, 1/250, 1/500, 1/1000)
#sensitivity or ISO
SV_value = (100, 200, 400, 800, 1600, 3200, 6400, 12800, 25600)
#exposure value
EV_value = (2.5, 5, 10, 20, 40, 80, 160, 320, 640, 1280, 2560)
TV_index = 3
SV_index = 0
AV_index = 2
pre_TV_index = 3
pre_SV_index = 0
pre_AV_index = 2
EV_index = 5
#actinometer part
i2c = board.I2C() # uses board.SCL and board.SDA
# i2c = board.STEMMA_I2C() # For using the built-in STEMMA QT connector on a microcontroller
sensor = adafruit_bh1750.BH1750(i2c)
print("%.2f Lux" % sensor.lux)
# LED setup.
led = DigitalInOut(board.LED)
# For QT Py M0. QT Py M0 does not have a D13 LED, so you can connect an external LED instead.
# led = DigitalInOut(board.SCK)
led.direction = Direction.OUTPUT
# For Gemma M0, Trinket M0, Metro M0 Express, ItsyBitsy M0 Express, Itsy M4 Express, QT Py M0
switch_red = DigitalInOut(board.D1)
switch_red.direction = Direction.INPUT
switch_red.pull = Pull.UP
switch_blue = DigitalInOut(board.D2)
switch_blue.direction = Direction.INPUT
switch_blue.pull = Pull.UP
main_group = displayio.Group()
#TV_value_text = "Exposure Time"
TV_value_text = "Exposure Time"
UpperLeft_text_area = TextBox(
terminalio.FONT,
110,
30,
align=TextBox.ALIGN_CENTER,
text=TV_value_text,
background_color=0xFF00FF,
color=0x000000,
scale=1,
)
UpperLeft_text_area.x = 0
UpperLeft_text_area.y = 10
main_group.append(UpperLeft_text_area)
#AV_value_text = "Aperture Value"
AV_value_text = "Aperture Value"
UpperRight_text_area = TextBox(
terminalio.FONT,
110,
30,
align=TextBox.ALIGN_CENTER,
text=AV_value_text,
background_color=0xFF00FF,
color=0x000000,
scale=1,
)
UpperRight_text_area.x = 130
UpperRight_text_area.y = 10
main_group.append(UpperRight_text_area)
#ISO_value_text = "ISO"
ISO_value_text = "ISO:\n 100"
MidLeft_text_area = TextBox(
terminalio.FONT,
110,
30,
align=TextBox.ALIGN_CENTER,
text=ISO_value_text,
background_color=0xFF00FF,
color=0x000000,
scale=1,
)
MidLeft_text_area.x = 0
MidLeft_text_area.y = 50
main_group.append(MidLeft_text_area)
#EV_value_text = "Exposure Value"
EV_value_text = "Exposure Value"
MidRight_text_area = TextBox(
terminalio.FONT,
110,
30,
align=TextBox.ALIGN_CENTER,
text=EV_value_text,
background_color=0xFFFF00,
color=0x000000,
scale=1,
)
MidRight_text_area.x = 130
MidRight_text_area.y = 50
main_group.append(MidRight_text_area)
#ISO_value_text = "Illuminance"
Illuminance_value_text = "Illuminance"
BottomLeft_text_area = TextBox(
terminalio.FONT,
110,
30,
align=TextBox.ALIGN_CENTER,
text=Illuminance_value_text,
background_color=0x00FFFF,
color=0x000000,
scale=1,
)
BottomLeft_text_area.x = 0
BottomLeft_text_area.y = 90
main_group.append(BottomLeft_text_area)
# #EV_value_text = "Exposure Value"
# EV_value_text = "Exposure Value"
# BottomRight_text_area = TextBox(
# terminalio.FONT,
# 110,
# 30,
# align=TextBox.ALIGN_CENTER,
# text=EV_value_text,
# background_color=0xFFFF00,
# color=0x000000,
# scale=1,
# )
#
# BottomRight_text_area.x = 130
# BottomRight_text_area.y = 90
# main_group.append(BottomRight_text_area)
board.DISPLAY.root_group = main_group
# WiFi
try:
print("Connecting to %s" % os.getenv("CIRCUITPY_WIFI_SSID"))
wifi.radio.connect(os.getenv("CIRCUITPY_WIFI_SSID"), os.getenv("CIRCUITPY_WIFI_PASSWORD"))
print("Connected to %s!" % os.getenv("CIRCUITPY_WIFI_SSID"))
# Wi-Fi connectivity fails with error messages, not specific errors, so this except is broad.
except Exception as e: # pylint: disable=broad-except
print("Failed to connect to WiFi. Error:", e, "\nBoard will hard reset in 30 seconds.")
time.sleep(30)
microcontroller.reset()
# Initialise NeoPixel
pixel = neopixel.NeoPixel(board.NEOPIXEL, 1, brightness=0.3)
# Define callback functions which will be called when certain events happen.
def connected(client):
print("Connected to Adafruit IO! Listening for NeoPixel changes...")
# Subscribe to Adafruit IO feed called "neopixel"
client.subscribe("neopixel")
client.subscribe("TV")
client.subscribe("SV")
client.subscribe("AV")
def message(client, feed_id, payload): # pylint: disable=unused-argument
"""
函数功能:
根据不同的feed_id处理接收到的payload,并更新相应的界面文本区域显示以及记录相关索引值。
参数说明:
- client:当前未在函数内使用,可能后续会用到或者是符合接口规范传入的参数。
- feed_id:用于标识消息来源的字符串,取值可能为"TV"、"SV"、"AV"等,用于区分不同类型的数据。
- payload:承载具体数据内容的对象,在函数内会尝试转换为整数作为索引去获取对应_value列表中的元素。
"""
global TV_index
global SV_index
global AV_index
global EV_index
try:
payload_as_int = int(payload)
except ValueError:
print(f"无法将payload {payload} 转换为整数,跳过当前处理步骤。")
return
if feed_id == "TV":
"""
处理feed_id为"TV"的情况:
1. 打印接收到新值的提示信息。
2. 更新UpperLeft_text_area的文本内容。
3. 记录TV_index的值。
"""
try:
print("Feed {0} received new value: {1}".format(feed_id, TV_value[payload_as_int]))
UpperLeft_text_area.text = "{0}:\n {1} - {2}s".format(feed_id, payload_as_int, TV_value[payload_as_int])
TV_index = payload_as_int
except IndexError:
print(f"索引 {payload_as_int} 超出TV_value列表范围,跳过更新操作。")
elif feed_id == "SV":
"""
处理feed_id为"SV"的情况:
1. 打印接收到新值的提示信息。
2. 更新MidLeft_text_area的文本内容。
3. 记录SV_index的值。
"""
try:
print("Feed {0} received new value: {1}".format(feed_id, SV_value[payload_as_int]))
MidLeft_text_area.text = "{0}:\n {1} - ISO{2}".format(feed_id, payload_as_int, SV_value[payload_as_int])
SV_index = payload_as_int
except IndexError:
print(f"索引 {payload_as_int} 超出SV_value列表范围,跳过更新操作。")
elif feed_id == "AV":
"""
处理feed_id为"AV"的情况:
1. 打印接收到新值的提示信息。
2. 更新UpperRight_text_area的文本内容。
3. 记录AV_index的值。
"""
try:
print("Feed {0} received new value: {1}".format(feed_id, AV_value[payload_as_int]))
UpperRight_text_area.text = "{0}:\n {1} - F/{2}".format(feed_id, payload_as_int, AV_value[payload_as_int])
AV_index = payload_as_int
except IndexError:
print(f"索引 {payload_as_int} 超出AV_value列表范围,跳过更新操作。")
# # 确保前面的索引变量都已正确赋值(非默认值)后再进行计算和更新MidRight_text_area的文本内容
# if TV_index!= -1 and SV_index!= -1 and AV_index!= -1:
# try:
# EV_index = TV_index + AV_index - SV_index
# if EV_index < 0:
# EV_index = 0
# elif EV_index >= len(EV_value):
# EV_index = len(EV_value) - 1
# MidRight_text_area.text = "Exposure Value : {}Lux".format(EV_value[EV_index])
# except IndexError:
# print(f"计算得到的索引 {TV_index + AV_index - SV_index} 超出EV_value列表范围,跳过更新操作。")
# else:
# print("部分索引变量未正确赋值,无法更新MidRight_text_area的文本内容。")
# Create a socket pool
pool = socketpool.SocketPool(wifi.radio)
# Initialize a new MQTT Client object
mqtt_client = MQTT.MQTT(
broker="io.adafruit.com",
username=os.getenv("ADAFRUIT_AIO_USERNAME"),
password=os.getenv("ADAFRUIT_AIO_KEY"),
socket_pool=pool,
ssl_context=ssl.create_default_context(),
)
# Initialize Adafruit IO MQTT "helper"
io = IO_MQTT(mqtt_client)
# Set up the callback methods above
io.on_connect = connected
io.on_message = message
timestamp = 0
red_pre_value = True
blue_pre_value = True
red_current_value = True
blue_current_value = True
while True:
try:
# If Adafruit IO is not connected...
if not io.is_connected:
# Connect the client to the MQTT broker.
print("Connecting to Adafruit IO...")
io.connect()
# Explicitly pump the message loop.
io.loop()
if (time.monotonic() - timestamp) >= 10:
print("%.2f Lux" % sensor.lux)
io.publish("bv", "%.2f Lux" % sensor.lux)
io.publish("actinometer.ev", str(AV_index + TV_index - SV_index))
print("AV_index: {0}; TV_index: {1}; SV_index: {2}; EV_index: {3}".format(AV_index, TV_index, SV_index, EV_index))
BottomLeft_text_area.text = "Illuminance:/n %.2f Lux" % sensor.lux
timestamp = time.monotonic()
red_current_value = switch_red.value
if red_pre_value and not red_current_value:
if AV_index >= len(AV_value) -1:
AV_index = 0
else :
AV_index += 1
UpperRight_text_area.text = "Aperture Value:\n {0} F/{1}".format(AV_index, AV_value[AV_index])
print("AV_index: {0}; TV_index: {1}; SV_index: {2}".format(AV_index, TV_index, SV_index))
red_pre_value = red_current_value
blue_current_value = switch_blue.value
if blue_pre_value and not blue_current_value:
if TV_index >= len(TV_value) -1:
TV_index = 0
else :
TV_index += 1
UpperLeft_text_area.text = "Exposure Time:\n {0}s".format(TV_index, TV_value[TV_index])
print("AV_index: {0}; TV_index: {1}; SV_index: {2}".format(AV_index, TV_index, SV_index))
blue_pre_value = blue_current_value
time.sleep(0.01)
# 确保前面的索引变量都已正确赋值(非默认值)后再进行计算和更新MidRight_text_area的文本内容
if TV_index!= pre_TV_index or SV_index!= pre_SV_index or AV_index!= pre_AV_index:
pre_TV_index = TV_index
pre_SV_index = SV_index
pre_AV_index = AV_index
try:
EV_index = TV_index + AV_index - SV_index
if EV_index < 0:
EV_index = 0
elif EV_index >= len(EV_value):
EV_index = len(EV_value) - 1
MidRight_text_area.text = "Exposure Value : {}Lux".format(EV_value[EV_index])
except IndexError:
print(f"计算得到的索引 {TV_index + AV_index - SV_index} 超出EV_value列表范围,跳过更新操作。")
if sensor.lux > EV_value[EV_index]:
print("Sweep from 0 to 1.0 fractionally")
fraction = 0.0
while fraction < 1.0:
servo.fraction = fraction
fraction += 0.01
time.sleep(0.01)
if switch_blue.value:
led.value = False
else:
led.value = True
# Adafruit IO fails with internal error types and WiFi fails with specific messages.
# This except is broad to handle any possible failure.
except Exception as e: # pylint: disable=broad-except
print("Failed to get or send data, or connect. Error:", e,
"\nBoard will hard reset in 30 seconds.")
time.sleep(30)
microcontroller.reset()--------------------------------------------------------
参考链接:
我要赚赏金
