这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » DIY与开源设计 » 电子DIY » Let‘s do 第三季[电子测光表] 进阶任务+控制舵机+菜单切换测光模式

共1条 1/1 1 跳转至

Let‘s do 第三季[电子测光表] 进阶任务+控制舵机+菜单切换测光模式

助工
2024-11-26 06:22:10     打赏

简介



本章节将尝试完成电子测光表的进阶任务。 实际上一共三个进阶任务。 即 

1- 使用手机控制舵机拍照 

2- 菜单切换BH1750测光模式

3- 蓝牙控制舵机


但是实际上如果要使用Circuit Python的话, 这个ESP32S3 - TFT feature 开发板并不支持蓝牙。

image.png

所以如果不使用Arduino , 或者其他固件的话即无法使用蓝牙功能, 或者像群里某个大佬一样实现使用WEB网页的方式控制, 不过这种较为麻烦。所以本文将带来,BH1750 切换测光模式的实现和 使用按钮控制舵机的实现。


BH1750 测光菜单切换测光模式和分辨率




image.png


BH1750 的测光,在库定义中一共有三个模式, 第一个是停止模式, 第二个是连续测光,第三个是单次测光。

对于分辨率的设置一共也是有三种,反别是Low (低), MID (中) 和, HIGH (高)


image.png

我们可以修改代码, 根据按键的状态来切换分辨率和测光的模式。


直接在之前的code.py 中修改, 主要是增加了按键的控制切换。


import time
import math
import board
import adafruit_bh1750
import digitalio
import terminalio
from adafruit_display_text import bitmap_label
import displayio

# 初始化 I2C 和 BH1750 传感器
i2c = board.I2C()  # 使用板载 SCL 和 SDA
sensor = adafruit_bh1750.BH1750(i2c)

# 计算 EV (曝光值) 的函数
def calculate_ev(lux):
    return 2 + math.log(lux / 10, 2) if lux > 0 else 0

# 计算快门速度的函数,基于 EV、光圈值和 ISO
def calculate_shutter_speed(ev, aperture, iso):
    # Shutter speed = (aperture ^ 2) / (ISO * (2 ^ EV))
    return (aperture ** 2) / (iso * (2 ** ev))

# 初始化按钮 D13 和 D12
button_up = digitalio.DigitalInOut(board.D13)
button_up.direction = digitalio.Direction.INPUT
button_up.pull = digitalio.Pull.UP  # 启用上拉电阻

button_down = digitalio.DigitalInOut(board.D12)
button_down.direction = digitalio.Direction.INPUT
button_down.pull = digitalio.Pull.UP  # 启用上拉电阻

# 初始化显示
group = displayio.Group()
board.DISPLAY.root_group = group

# 创建不同文本区域的标签,设置 scale=1 统一字体大小
shutter_label = bitmap_label.Label(terminalio.FONT, text="", scale=1)
shutter_label.x = 10
shutter_label.y = 10
group.append(shutter_label)

aperture_label = bitmap_label.Label(terminalio.FONT, text="", scale=1)
aperture_label.x = 10
aperture_label.y = 30
group.append(aperture_label)

iso_label = bitmap_label.Label(terminalio.FONT, text="", scale=1)
iso_label.x = 10
iso_label.y = 50
group.append(iso_label)

mode_label = bitmap_label.Label(terminalio.FONT, text="", scale=1)
mode_label.x = 10
mode_label.y = 70
group.append(mode_label)

resolution_label = bitmap_label.Label(terminalio.FONT, text="", scale=1)
resolution_label.x = 10
resolution_label.y = 90
group.append(resolution_label)

# 初始设置
aperture = 3.5  # 固定光圈值
iso = 100       # 初始 ISO 值

# 定义 BH1750 测光模式
modes = [
    "Continuous",  # 连续高分辨率模式
    "One Time",    # 单次高分辨率模式
    "STOP"         # 停止模式
]
current_mode_index = 0  # 默认使用 "Continuous" 模式

# 定义 BH1750 分辨率模式
resolutions = [
    "LOW",  # 低分辨率模式
    "MID",  # 中分辨率模式
    "HIGH"  # 高分辨率模式
]
current_resolution_index = 0  # 默认使用 "LOW" 分辨率模式

# 切换测光模式的函数
def switch_mode():
    global current_mode_index
    current_mode_index = (current_mode_index + 1) % len(modes)
    # 根据选择的模式设置传感器
    if current_mode_index == 0:
        sensor.mode = adafruit_bh1750.Mode.CONTINUOUS
    elif current_mode_index == 1:
        sensor.mode = adafruit_bh1750.Mode.ONE_SHOT

# 切换分辨率模式的函数
def switch_resolution():
    global current_resolution_index
    current_resolution_index = (current_resolution_index + 1) % len(resolutions)
    # 根据选择的分辨率设置传感器
    if current_resolution_index == 0:
        sensor.resolution = adafruit_bh1750.Resolution.LOW
    elif current_resolution_index == 1:
        sensor.resolution = adafruit_bh1750.Resolution.MID
    elif current_resolution_index == 2:
        sensor.resolution = adafruit_bh1750.Resolution.HIGH

# 主循环
while True:
    lux = sensor.lux  # 从传感器读取 Lux 值
    ev = calculate_ev(lux)  # 从 Lux 计算 EV 值

   
    # 切换测光模式(使用按钮 D12)
    if not button_down.value:  # D12 按钮被按下
        switch_mode()
        time.sleep(0.2)  # 防抖延迟

    # 切换分辨率模式(使用按钮 D13)
    if not button_up.value:  # D13 按钮被按下
        switch_resolution()
        time.sleep(0.2)  # 防抖延迟

    # 基于固定光圈计算快门速度
    shutter_speed = calculate_shutter_speed(ev, aperture, iso)

    # 更新显示
    shutter_label.text = f"Shutter: 1/{int(1 / shutter_speed)}s" if shutter_speed > 0 else "Shutter: --"
    aperture_label.text = f"Aperture: f/{aperture:.1f}"  # 固定光圈值
    iso_label.text = f"ISO: {iso}"
    mode_label.text = f"Mode: {modes[current_mode_index]}"
    resolution_label.text = f"Resolution: {resolutions[current_resolution_index]}"

    time.sleep(0.1)


这样当用户按下按键的时候就可以切换BH1750的测光模式和分辨率。

0fcf94ddb0249ac0fc999aa22f43d75.jpg


360度舵机控制




Adafruit家提供的库非常丰富, 我们可以下载adafruit-circuitpython-bundle-9.x-mpy-20241121  地址: https://github.com/adafruit/Adafruit_CircuitPython_Bundle/releases  这个压缩包里包括了几乎所有的Cpy的库和demo,我们需要用到的是舵机的相关库。

image.png

把这个文件夹, 拷贝到开发板的lib目录下。

然后可以使用下述代码来控制舵机的正转和反转。


import time
import board
import digitalio
import pwmio

# 设置 D12 和 D13 按钮
button_rotate = digitalio.DigitalInOut(board.D12)
button_rotate.direction = digitalio.Direction.INPUT
button_rotate.pull = digitalio.Pull.UP  # 上拉电阻

button_stop = digitalio.DigitalInOut(board.D13)
button_stop.direction = digitalio.Direction.INPUT
button_stop.pull = digitalio.Pull.UP  # 上拉电阻

# 设置 D11 用于 PWM 信号控制舵机
pwm = pwmio.PWMOut(board.D11, duty_cycle=2**15, frequency=50)

# 变量来跟踪舵机当前的旋转方向,True 表示正转,False 表示反转
is_forward = True

# 控制舵机旋转的函数
def move_servo(duty_cycle):
    pwm.duty_cycle = duty_cycle  # 调整 PWM 占空比来控制旋转

# 主循环
while True:
    # 检查 D12 按钮是否被按下,切换方向
    if not button_rotate.value:  # 按下时切换方向
        if is_forward:
            print("Switching to reverse")
            move_servo(8000)  
        else:
            print("Switching to forward")
            move_servo(4000)  
        is_forward = not is_forward  # 切换方向
        time.sleep(0.2)  # 防抖延迟

    # 如果 D13 按钮被按下,舵机停止
    elif not button_stop.value:
        print("Stopping")
        move_servo(5000)  # 停止,占空比接近 1.5ms(相对于 50Hz 的频率)
        time.sleep(0.2)  # 防抖延迟


由于这个舵机是一个360度的舵机,所有没办法控制具体的角度, 只能通过PWM的占空比等来调整方向和速度。上文中代码设置的反转的速度是正转的两倍, 我们可以通过控制修改这个占空比来调整速度。

64463c985a98a0e760c47a09e14670b.jpg


屏幕显示部分

5e8d1e0db6fadc9367eb4a59621ded8.jpg



- 更新日期 2024 / 12 /2 日

- 更新日志 : 增加了WIFI链接和MQTT集成Home Assistant, 通过Home Assistant实现控制舵机和数据的回显。


代码如下

import time
import math
import board
import adafruit_bh1750
import digitalio
import terminalio
from adafruit_display_text import bitmap_label
import displayio
import os
import ssl
import socketpool
import wifi
import json
import pwmio

import adafruit_minimqtt.adafruit_minimqtt as MQTT

aio_username = "你的MQTT账号"
aio_key = "你的MQTT密码"

print(aio_username)
wifi.radio.connect("你的wifi账号","你的wifi密码")
print(wifi.radio.ipv4_address)

topic = "user/motor"
sensor_topic = "user/sensor"


# Define callback methods which are called when events occur
def connected(client, userdata, flags, rc):
    # This function will be called when the client is connected
    # successfully to the broker.
    print(f"Connected to MQTT Server! Listening for topic changes on {topic}")
    # Subscribe to all changes on the onoff_feed.
    client.subscribe(topic)
    print("Connected to motor topic")
    client.subscribe(sensor_topic)
    print("Connected to sensor topic")


def disconnected(client, userdata, rc):
    # This method is called when the client is disconnected
    print("Disconnected from Adafruit IO!")

is_forward = True




pwm = pwmio.PWMOut(board.D11, duty_cycle=2**15, frequency=50)
# 控制舵机旋转的函数
def move_servo(duty_cycle):
    pwm.duty_cycle = duty_cycle  # 调整 PWM 占空比来控制旋转



def message(client, topic, message):
    print(f"New message on topic {topic}: {message}")
    global is_forward
    if topic == "user/motor":
        if message == "Switch":
            if is_forward:
                move_servo(8000)  # 如果是前进方向,设置 PWM 占空比
            else:
                move_servo(4000)  # 如果是后退方向,设置 PWM 占空比
            is_forward = not is_forward  # 切换方向
        elif message == "OFF":
            print("Turning servo OFF")
            move_servo(5000)  # 关闭舵机(可根据需求调整)
            time.sleep(0.2)

       
    

pool = socketpool.SocketPool(wifi.radio)
ssl_context = ssl.create_default_context()


# Set up a MiniMQTT Client
mqtt_client = MQTT.MQTT(
    broker="192.168.1.113",
    port=1883,
    socket_pool=pool,
    ssl_context=None,
)

# Setup the callback methods above
mqtt_client.on_connect = connected
mqtt_client.on_disconnect = disconnected
mqtt_client.on_message = message


# Connect the client to the MQTT broker.
print("Connecting to MQTT Server...")
mqtt_client.connect()
print("Connected...")
photocell_val = 0


# 变量来跟踪舵机当前的旋转方向,True 表示正转,False 表示反转
is_forward = True



# 初始化 I2C 和 BH1750 传感器
i2c = board.I2C()  # 使用板载 SCL 和 SDA
sensor = adafruit_bh1750.BH1750(i2c)

# 计算 EV (曝光值) 的函数
def calculate_ev(lux):
    return 2 + math.log(lux / 10, 2) if lux > 0 else 0

# 计算快门速度的函数,基于 EV、光圈值和 ISO
def calculate_shutter_speed(ev, aperture, iso):
    # Shutter speed = (aperture ^ 2) / (ISO * (2 ^ EV))
    return (aperture ** 2) / ((2 ** ev))

# 初始化按钮 D13 和 D12
button_up = digitalio.DigitalInOut(board.D13)
button_up.direction = digitalio.Direction.INPUT
button_up.pull = digitalio.Pull.UP  # 启用上拉电阻

button_down = digitalio.DigitalInOut(board.D12)
button_down.direction = digitalio.Direction.INPUT
button_down.pull = digitalio.Pull.UP  # 启用上拉电阻

# 初始化显示
group = displayio.Group()
board.DISPLAY.root_group = group

# 创建不同文本区域的标签,设置 scale=1 统一字体大小
shutter_label = bitmap_label.Label(terminalio.FONT, text="", scale=1)
shutter_label.x = 10
shutter_label.y = 10
group.append(shutter_label)

aperture_label = bitmap_label.Label(terminalio.FONT, text="", scale=1)
aperture_label.x = 10
aperture_label.y = 30
group.append(aperture_label)

iso_label = bitmap_label.Label(terminalio.FONT, text="", scale=1)
iso_label.x = 10
iso_label.y = 50
group.append(iso_label)

mode_label = bitmap_label.Label(terminalio.FONT, text="", scale=1)
mode_label.x = 10
mode_label.y = 70
group.append(mode_label)

resolution_label = bitmap_label.Label(terminalio.FONT, text="", scale=1)
resolution_label.x = 10
resolution_label.y = 90
group.append(resolution_label)

lux_label = bitmap_label.Label(terminalio.FONT, text="", scale=1)
lux_label.x = 10
lux_label.y = 110  # 显示在最后一行
group.append(lux_label)

# 初始设置
aperture = 3.5  # 固定光圈值
iso = 100       # 初始 ISO 值

# 定义 BH1750 测光模式
modes = [
    "Continuous",  # 连续高分辨率模式
    "One Time",    # 单次高分辨率模式
    "STOP"         # 停止模式
]
current_mode_index = 0  # 默认使用 "Continuous" 模式

# 定义 BH1750 分辨率模式
resolutions = [
    "LOW",  # 低分辨率模式
    "MID",  # 中分辨率模式
    "HIGH"  # 高分辨率模式
]
current_resolution_index = 0  # 默认使用 "LOW" 分辨率模式

# 切换测光模式的函数
def switch_mode():
    global current_mode_index
    current_mode_index = (current_mode_index + 1) % len(modes)
    # 根据选择的模式设置传感器
    if current_mode_index == 0:
        sensor.mode = adafruit_bh1750.Mode.CONTINUOUS
    elif current_mode_index == 1:
        sensor.mode = adafruit_bh1750.Mode.ONE_SHOT

# 切换分辨率模式的函数
def switch_resolution():
    global current_resolution_index
    current_resolution_index = (current_resolution_index + 1) % len(resolutions)
    # 根据选择的分辨率设置传感器
    if current_resolution_index == 0:
        sensor.resolution = adafruit_bh1750.Resolution.LOW
    elif current_resolution_index == 1:
        sensor.resolution = adafruit_bh1750.Resolution.MID
    elif current_resolution_index == 2:
        sensor.resolution = adafruit_bh1750.Resolution.HIGH

# 主循环
while True:
    mqtt_client.loop(timeout=1)
    
    lux = sensor.lux  # 从传感器读取 Lux 值
    ev = calculate_ev(lux)  # 从 Lux 计算 EV 值

   
    # 切换测光模式(使用按钮 D12)
    if not button_down.value:  # D12 按钮被按下
        switch_mode()
        time.sleep(0.2)  # 防抖延迟

    # 切换分辨率模式(使用按钮 D13)
    if not button_up.value:  # D13 按钮被按下
        switch_resolution()
        time.sleep(0.2)  # 防抖延迟

    # 基于固定光圈计算快门速度
    shutter_speed = calculate_shutter_speed(ev, aperture, iso)

    # 更新显示
    shutter_label.text = f"Shutter: 1/{int(1 / shutter_speed)}s" if shutter_speed > 0 else "Shutter: --"
    aperture_label.text = f"Aperture: f/{aperture:.1f}"  # 固定光圈值
    iso_label.text = f"ISO: {iso}"
    mode_label.text = f"Mode: {modes[current_mode_index]}"
    resolution_label.text = f"Resolution: {resolutions[current_resolution_index]}"
    lux_label.text = f"Light: {lux:.2f} Lux"  # 更新光照值显示
    data = {
        "lux": lux,
        "ev": ev,
        "shutter_speed": f"1/{int(1 / shutter_speed)}" if shutter_speed > 0 else "--",
        "aperture": aperture,
        "iso": iso,
        "mode": modes[current_mode_index],
        "resolution": resolutions[current_resolution_index],
    }
    mqtt_client.publish(sensor_topic, json.dumps(data))  # 发送 JSON 数据

    time.sleep(0.1)


效果展示

image.png


手机端效果展示

356d49cb049b52cdd20b8c041dedc30.jpg


视频展示

https://www.bilibili.com/video/BV1TD6MYVET1/?vd_source=fa81ee8dac5a78e9ccb692c6642f6fe2

EETV视频链接:https://v.eepw.com.cn/video/play/id/16197 


共1条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]