找回密码
 立即注册

微信扫码登录

搜索
楼主: dreameryy

[照明] 基于ESP32-C3的家用双色调光灯。

[复制链接]

8

主题

123

回帖

650

积分

高级会员

积分
650
金钱
519
HASS币
0
发表于 前天 19:02 | 显示全部楼层
我也喜欢diy更有乐趣。可惜没看到电路图
回复

使用道具 举报

2

主题

12

回帖

75

积分

论坛DIY达人

积分
75
金钱
61
HASS币
0
 楼主| 发表于 昨天 11:43 | 显示全部楼层
taida 发表于 2025-11-10 17:13
楼主说的好,这个花了我差不多一张毛爷爷,但是过程很好,反复折腾才是diy的乐趣
...

你这个高级啊   开源一下啊
回复

使用道具 举报

2

主题

12

回帖

75

积分

论坛DIY达人

积分
75
金钱
61
HASS币
0
 楼主| 发表于 昨天 11:46 | 显示全部楼层
hxck 发表于 2025-11-10 16:21
这个好,必须点赞。能带动多大W数的

目前测试 能带8-48W 恒流输出 电流200-350ma  通过引脚电阻设定 电压最高好像是180V
回复

使用道具 举报

2

主题

12

回帖

75

积分

论坛DIY达人

积分
75
金钱
61
HASS币
0
 楼主| 发表于 昨天 11:48 | 显示全部楼层
yj82871002 发表于 2025-11-10 16:19
能实现开灯缓亮么,有几个筒灯想要这个功能。晚上开灯不刺眼,另外缓亮也比较有氛围感。分享下电路图么? ...

可以的好像是这个参数 PWM下默认好像是1秒  我现在没设置任何参数 就是大概1秒的缓冲时间 flash_transition_length(可选,时间):调用闪光灯时要使用的过渡长度。 默认为 。0s
回复

使用道具 举报

28

主题

138

回帖

1117

积分

金牌会员

积分
1117
金钱
951
HASS币
0
发表于 昨天 12:52 | 显示全部楼层
本帖最后由 taida 于 2025-11-11 13:10 编辑
dreameryy 发表于 2025-11-11 11:43
你这个高级啊   开源一下啊

彩屏旋钮只负责发送动作给ha,在nodered中实现的场景切换,我做了调光灯和窗帘的调试,代码也许有不完善的地方,实现了单击开关灯或者窗帘,双击切换到下一盏灯,三击切换窗帘,长按切换色温,旋转控制亮度和开合度,代码是用的esphome,做了休眠和唤醒,代码是用gmini修改的,也许有不足的地方

esphome:
  name: my-display-encory
  friendly_name: 旋钮彩屏
  
  on_boot:
    then:
      - logger.log: "设备启动中,开启屏幕电源..."
      
      # 1. 唤醒后立即开启屏幕 VCC 供电 (GPIO23 -> HIGH)
      - output.turn_on: display_power_pin 
      - delay: 50ms # 给予屏幕控制器启动时间
      
      # 2. 启动空闲休眠定时器 (120秒)
      - script.execute: start_sleep_timer 
      
      # 3. 初始化文本传感器
      - text_sensor.template.publish:
          id: current_display_name
          state: "旋钮控制台"
      - text_sensor.template.publish:
          id: current_display_state
          state: "未知"
      - text_sensor.template.publish:
          id: button_action_sensor
          state: "无" 
      - light.turn_on: back_light 
      
      # 4. 启动时发布 MQTT 切换事件,让 Node-RED 推送初始 UI
      - mqtt.publish:
          topic: "esphome/display/mode_switch_intent"
          payload: "INIT" 
          qos: 1
          retain: true
      
esp32:
  board: esp32-s3-devkitc-1
  framework:
    type: arduino
    
logger:
  level: DEBUG 
  baud_rate: 1159200

api:
ota:
  - platform: esphome

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password
  power_save_mode: none
  ap:
    ssid: "my-display-encory"
    password: ""

web_server:
  port: 80

mqtt:
  id: mqtt_client 
  broker: 192.168.*.*
  port: 1883
  username: *****
  password: *****

captive_portal:

preferences:
  flash_write_interval: 5min

# ----------------------------------------------------
# 深度休眠组件 (最终修正:使用 EXT1 匹配 HIGH->LOW 唤醒逻辑)
# ----------------------------------------------------
deep_sleep:
  id: deep_sleep_1
  
  # 您的唤醒逻辑:空闲 HIGH (3.3V) -> 唤醒 LOW (0V)。
  
  # 【关键】使用 ESP32-S3 专用的 EXT1 唤醒机制
  esp32_ext1_wakeup:
    pins: 
      - number: GPIO4
        mode: INPUT_PULLDOWN
    mode: ANY_HIGH # 监听 LOW 电平唤醒(与按钮按下时的 0V 匹配)

# ====================================================================
# 字体 / 全局变量
# ====================================================================
font:
  - file: "MaterialDesignIconsDesktop.ttf"
    id: mdi_42
    size: 42
    bpp: 4
    glyphs: 
      - "\U000F0335" # Lightbulb on
      - "\U000F0336" # Lightbulb off
      - "\U000F01BE" # Curtain (部分展开/默认)
      - "\U000F01BF" # Curtain Closed (关闭)
      - "\U000F01BD" # Curtain Open (打开)
  - file: "MaterialDesignIconsDesktop.ttf"
    id: mdi_72 
    size: 72
    bpp: 4
    glyphs: 
      - "\U000F0335" 
      - "\U000F0336" 
      - "\U000F01BE" 
      - "\U000F01BF" 
      - "\U000F01BD" 
  - file: "NotoSansSC-Regular.ttf"
    id: zh_cn_18
    size: 18
    bpp: 4
    glyphs:
      - "0123456789%.: ONF" 
      - "设置信息温度湿时间电费水灌岸卡室号内地址按钮操作旋值状态元开关显示器实体单击双长顺逆针三当前未连接知位离线测试色灯启闭打错误备百分比已/!控制台冷暖客厅主卧次B大窗帘名称"

globals:
  - id: button_press_start
    type: int
    initial_value: '0'
    restore_value: no
  - id: button_is_pressed
    type: bool
    initial_value: 'false'
    restore_value: no
  - id: rotation_in_progress
    type: bool
    initial_value: 'false' 
    restore_value: no
  - id: single_click_counter
    type: int
    initial_value: '0'
  - id: double_click_counter
    type: int
    initial_value: '0'
  - id: triple_click_counter
    type: int
    initial_value: '0'

# ====================================================================
# 显示配置 / OUTPUT / LIGHT
# ====================================================================
spi:
  id: spi_bus_0
  clk_pin: GPIO12
  mosi_pin: GPIO13

display:
  - platform: mipi_spi
    id: rotary_display
    model: GC9A01A 
    spi_id: spi_bus_0
    
    cs_pin: GPIO10
    dc_pin: GPIO9
    reset_pin: GPIO8
    
    dimensions:
      width: 240
      height: 240
    rotation: 90 
    
    invert_colors: false 

lvgl:
  id: lvgl_display
  displays:
    - rotary_display
  
  on_ready:
    then:
      - lambda: |-
          #include "lvgl.h"
          
          lv_obj_set_style_bg_color(lv_scr_act(), lv_color_make(0xFF, 0xFF, 0xFF), 0);
          lv_obj_set_style_bg_opa(lv_scr_act(), LV_OPA_COVER, 0); 
          
          lv_obj_set_style_arc_color(id(main_arc), lv_color_make(0xDF, 0xDF, 0xDF), LV_PART_MAIN); 
          lv_obj_set_style_arc_width(id(main_arc), 15, LV_PART_MAIN);

          lv_obj_set_style_arc_color(id(main_arc), lv_color_make(0x00, 0x66, 0xFF), LV_PART_INDICATOR); 
          lv_obj_set_style_arc_width(id(main_arc), 15, LV_PART_INDICATOR);
          
          lv_obj_set_style_bg_opacity(id(main_arc), 0, LV_PART_KNOB);
          lv_obj_set_style_border_width(id(main_arc), 0, LV_PART_KNOB);


  widgets:
    - arc:
        id: main_arc 
        width: 220 
        height: 220 
        align: center
        min_value: 0
        max_value: 100 
        value: 0
    - label:
        id: label_icon
        text: "\U000F0336" 
        align: center
        text_font: mdi_72 
        text_color: 0x7F7F7F 
        recolor: true
    - label:
        id: label_name
        text: "设备名称" 
        align: bottom_mid
        y: -40
        text_font: zh_cn_18 
        text_color: 0x7F7F7F 
    - label:
        id: label_percentage
        text: "0%" 
        align: bottom_mid
        y: -15
        text_font: zh_cn_18 
        text_color: 0x7F7F7F 
        
output:
  - platform: ledc
    pin: GPIO14
    id: backlight_output
    inverted: true
  - platform: gpio
    pin: GPIO7
    id: display_power_pin

light:
  - platform: monochromatic
    output: backlight_output
    name: "显示屏背光"
    id: back_light
#    restore_mode: ALWAYS_ON
    
# ====================================================================
# 传感器和逻辑配置
# ====================================================================
text_sensor:
  - platform: template
    name: "通用控制实体友好名称"
    id: current_display_name
  - platform: template
    name: "通用实体状态"
    id: current_display_state
  - platform: template
    name: "按钮动作"
    id: button_action_sensor
  - platform: template
    name: "旋转度"
    id: rotation_direction_sensor

  # --- WiFi 辅助传感器 (省略) ---
  - platform: wifi_info
    ip_address:
      name: "设备IP地址"
      update_interval: 60s 
    mac_address:
      name: "设备MAC地址"
      id: mac_address_sensor 
  - platform: wifi_info
    ssid:
      name: "WiFi名称"
      update_interval: 60s 
  - platform: wifi_info
    bssid:
      name: "路由器MAC地址"
      update_interval: 60s 
      
  # MQTT 订阅:用于接收名称
  - platform: mqtt_subscribe
    name: "MQTT 名称输入"
    id: mqtt_name_input
    topic: "esphome/display/name_output"
    qos: 0
    internal: true
    on_value:
      - text_sensor.template.publish: { id: current_display_name, state: !lambda 'return x;' }
      - lvgl.label.update: { id: label_name, text: !lambda 'return x;' }
            
  # MQTT 订阅:用于接收状态 (V70 修复点)
  - platform: mqtt_subscribe
    name: "MQTT 状态输入"
    id: mqtt_state_input
    topic: "esphome/display/state_output"
    qos: 0
    internal: true
    
    on_value:
      - script.stop: start_sleep_timer
      - script.execute: start_sleep_timer
      - if:
          condition:
            lambda: 'return !id(rotation_in_progress);' 
          then:
            - text_sensor.template.publish: { id: current_display_state, state: !lambda 'return x;' }
            - lambda: |-
                #include "lvgl.h"
                #include <algorithm> // 引入算法库
                #include <cctype>    // 引入字符类型库
                
                lv_color_t active_color = lv_color_make(0x00, 0x66, 0xFF); 
                lv_color_t inactive_color = lv_color_make(0x7F, 0x7F, 0x7F); 
                
                std::string input_state = x;
                std::string current_name = id(current_display_name).state;
                
                std::string lower_state = input_state;
                std::transform(lower_state.begin(), lower_state.end(), lower_state.begin(),
                    [](unsigned char c){ return std::tolower(c); });
                
                bool is_on_string = (lower_state == "开启" || lower_state == "打开" || lower_state == "on" || lower_state == "open" || lower_state == "opening" || lower_state == "closing"); 
                bool is_off_string = (lower_state == "关闭" || lower_state == "off" || lower_state == "closed");
                
                int incoming_int = atoi(input_state.c_str());

                // 窗帘判断标志
                bool is_curtain = (current_name.find("窗帘") != std::string::npos);

                // --- 逻辑分支 ---
                // 1. 关闭/不活动/0% (灰色)
                if (is_off_string || (incoming_int == 0 && !is_on_string)) { 
                    
                    lv_arc_set_value(id(main_arc), 0);
                    lv_label_set_text(id(label_percentage), "0%");
                    
                    lv_obj_set_style_text_color(id(label_icon), inactive_color, LV_PART_MAIN);
                    lv_obj_set_style_text_color(id(label_name), inactive_color, LV_PART_MAIN);
                    lv_obj_set_style_text_color(id(label_percentage), inactive_color, LV_PART_MAIN);
                    
                    if (is_curtain) { 
                        // 窗帘关闭时使用完全关闭的图标
                        lv_label_set_text(id(label_icon), "\U000F01BF"); 
                    } else {
                        lv_label_set_text(id(label_icon), "\U000F0336"); // 灯光关闭图标
                    }
                
                // 2. 百分比 (1-100%) (蓝色)
                } else if (incoming_int >= 1 && incoming_int <= 100) { 
                       
                    lv_arc_set_value(id(main_arc), incoming_int);
                    lv_label_set_text(id(label_percentage), (to_string(incoming_int) + "%").c_str());

                    lv_obj_set_style_text_color(id(label_icon), active_color, LV_PART_MAIN);
                    lv_obj_set_style_text_color(id(label_name), active_color, LV_PART_MAIN);
                    lv_obj_set_style_text_color(id(label_percentage), active_color, LV_PART_MAIN); 
                    
                    if (is_curtain) {
                        // 窗帘有百分比时使用部分展开的图标
                        lv_label_set_text(id(label_icon), "\U000F01BE"); 
                    } else {
                        lv_label_set_text(id(label_icon), "\U000F0335"); // 灯光开启图标
                    }
                
                // 3. 开启/打开/Open/Opening/Closing 状态 (蓝色)
                } else if (is_on_string) { 
                        
                    int current_arc_value = lv_arc_get_value(id(main_arc));
                    int default_value = 50; 
                    if (current_arc_value == 0) {
                        lv_arc_set_value(id(main_arc), default_value);
                        lv_label_set_text(id(label_percentage), (to_string(default_value) + "%").c_str());
                    }

                    lv_obj_set_style_text_color(id(label_icon), active_color, LV_PART_MAIN);
                    lv_obj_set_style_text_color(id(label_name), active_color, LV_PART_MAIN);
                    lv_obj_set_style_text_color(id(label_percentage), active_color, LV_PART_MAIN); 
                    
                    if (is_curtain) {
                        // 窗帘开启/活动时,如果百分比大于0,则显示部分展开的图标
                        // 否则(例如只有“打开”状态,没有百分比),也显示部分展开的图标
                        if (lv_arc_get_value(id(main_arc)) > 0) {
                            lv_label_set_text(id(label_icon), "\U000F01BE"); // 部分展开
                        } else {
                            lv_label_set_text(id(label_icon), "\U000F01BD"); // 全开图标 (更符合打开/开启的语义)
                        }
                    } else {
                        lv_label_set_text(id(label_icon), "\U000F0335"); // 灯光开启图标
                    }
                        
                }
                
                lv_obj_invalidate(lv_scr_act()); 


sensor:
  # --- 旋转编码器 (保持不变) ---
  - platform: rotary_encoder
    name: "旋钮值"
    id: rotary_sensor
    pin_a:
      number: GPIO2
      inverted: True
      mode:
        input: true
        pullup: true
    pin_b:
      number: GPIO5
      inverted: True
      mode:
        input: true
        pullup: true
    min_value: 0
    max_value: 25 
    resolution: 1
    publish_initial_value: true
    
    filters:
      - debounce: 50ms 
      - delta: 1.0 
      - throttle: 100ms
      
    on_value:
      - globals.set: { id: rotation_in_progress, value: 'true' }
      - script.stop: rotation_end_timer
      - script.execute: rotation_end_timer
      - script.stop: start_sleep_timer
      - script.execute: start_sleep_timer
      
      # UI 更新逻辑 (只基于旋钮值更新百分比和弧度)
      - lambda: |-
          #include "lvgl.h"
          
          int rotary_state = (int)id(rotary_sensor).state;
          int display_value = rotary_state * 4;
          
          std::string value_str = to_string(display_value);
          std::string text = value_str + "%";
          
          lv_color_t active_color = lv_color_make(0x00, 0x66, 0xFF); 
          lv_color_t inactive_color = lv_color_make(0x7F, 0x7F, 0x7F); 
          
          lv_arc_set_value(id(main_arc), display_value);
          lv_label_set_text(id(label_percentage), text.c_str());
          
          if (display_value > 0) {
            lv_obj_set_style_text_color(id(label_icon), active_color, LV_PART_MAIN);
            lv_obj_set_style_text_color(id(label_name), active_color, LV_PART_MAIN);
            lv_obj_set_style_text_color(id(label_percentage), active_color, LV_PART_MAIN); 
          } else {
            lv_obj_set_style_text_color(id(label_icon), inactive_color, LV_PART_MAIN);
            lv_obj_set_style_text_color(id(label_name), inactive_color, LV_PART_MAIN);
            lv_obj_set_style_text_color(id(label_percentage), inactive_color, LV_PART_MAIN); 
          }
          lv_obj_invalidate(lv_scr_act());

    on_clockwise:
      then:
        - if:
            condition:
              - lambda: 'return !id(button_is_pressed) && (millis() - id(button_press_start) > 20);'
            then:
              - text_sensor.template.publish: { id: rotation_direction_sensor, state: "顺时针" }
              
    on_anticlockwise:
      then:
        - if:
            condition:
              - lambda: 'return !id(button_is_pressed) && (millis() - id(button_press_start) > 20);'
            then:
              - text_sensor.template.publish: { id: rotation_direction_sensor, state: "逆时针" }
              
  - platform: wifi_signal
    name: "WiFi信号强度"
    update_interval: 60s 
    unit_of_measurement: "dBm"
    device_class: signal_strength
    entity_category: diagnostic

# ====================================================================
# 脚本:用于定时器和锁的释放 (保持不变)
# ====================================================================
script:
  - id: start_sleep_timer
    mode: restart
    then:
      - delay: 120s 
      - logger.log: "设备空闲 120s,准备关闭背光、断开屏幕电源并进入深度休眠。"
      
      - light.turn_off: back_light 
      - wifi.disable: 
      - output.turn_off: display_power_pin 
      - delay: 50ms
      
      - deep_sleep.enter: deep_sleep_1 
      
  - id: rotation_end_timer
    mode: restart
    then:
      - delay: 500ms
      - globals.set: { id: rotation_in_progress, value: 'false' }
      - logger.log: "旋转停止,MQTT 状态同步锁已释放。"

# ====================================================================
# 按钮配置 (保持不变)
# ====================================================================
binary_sensor:
  - platform: gpio
    name: "主按钮"
    id: main_button
    pin:
      number: GPIO6
      inverted: true
      mode:
        input: true
        pullup: true
    filters:
      - delayed_on: 100ms
      - delayed_off: 100ms
    on_press:
      then:
        - globals.set: { id: button_press_start, value: !lambda 'return millis();' }
        - globals.set: { id: button_is_pressed, value: 'true' }
        - script.stop: start_sleep_timer 
        - logger.log: "按钮按下,停止休眠定时器"
    on_release:
      then:
        - globals.set: { id: button_is_pressed, value: 'false' }
        - logger.log: "按钮释放,重新启动休眠定时器"
        - script.execute: start_sleep_timer 
    
    on_multi_click:
      - timing:
          - ON for at most 1s
          - OFF for at least 0.5s
        then:
          - logger.log: "检测到单击"
          - lambda: |
              id(single_click_counter)++;
              id(button_action_sensor).publish_state("单击_" + to_string(id(single_click_counter)));
          
      - timing:
          - ON for at most 1s
          - OFF for at most 1s
          - ON for at most 1s
          - OFF for at least 0.2s
        then:
          - logger.log: "检测到双击"
          - lambda: |
              id(double_click_counter)++;
              id(button_action_sensor).publish_state("双击_" + to_string(id(double_click_counter)));
              
      - timing:
          - ON for at most 1s
          - OFF for at most 1s
          - ON for at most 1s
          - OFF for at most 1s
          - ON for at most 1s
          - OFF for at least 0.2s
        then:
          - logger.log: "检测到三击"
          - lambda: |
              id(triple_click_counter)++;
              id(button_action_sensor).publish_state("三击_" + to_string(id(triple_click_counter)));
              
      - timing:
          - ON for 1s to 2s
          - OFF for at least 0.5s
        then:
          - logger.log: "检测到长按"
          - text_sensor.template.publish: { id: button_action_sensor, state: "长按" }

# --- 辅助传感器和开关 ---
switch:
  - platform: restart
    name: "设备重启"    

你可以参考一下:

PCB参照下面的大佬链接按照自己需要的修改的:
https://oshwhub.com/yang-z-b/cai-ping-xuan-niu-esp32s3-hub-v1
image.png image.png image.png
学画PCB有1个月,排线这些不是很明白,将就了







flows (27).rar

9.17 KB, 下载次数: 1

回复

使用道具 举报

2

主题

12

回帖

75

积分

论坛DIY达人

积分
75
金钱
61
HASS币
0
 楼主| 发表于 昨天 13:26 | 显示全部楼层
taida 发表于 2025-11-11 12:52
彩屏旋钮只负责发送动作给ha,在nodered中实现的场景切换,我做了调光灯和窗帘的调试,代码也许有不完善的 ...

谢谢大佬分享  我研究一下
回复

使用道具 举报

hxck 手机认证

4

主题

122

回帖

900

积分

高级会员

积分
900
金钱
774
HASS币
0
发表于 昨天 15:44 | 显示全部楼层
dreameryy 发表于 2025-11-11 11:46
目前测试 能带8-48W 恒流输出 电流200-350ma  通过引脚电阻设定 电压最高好像是180V ...

如果要带更大的是不是要改动电路板了
回复

使用道具 举报

194

主题

2947

回帖

9580

积分

元老级技术达人

积分
9580
金钱
6424
HASS币
80
发表于 昨天 16:20 | 显示全部楼层
yj82871002 发表于 2025-11-10 16:24
菜市场买条鱼也不贵,还不是有大把钓鱼佬花大价钱买杆子打窝。个人需求不同啊,不光追求功能还享受DIY的 ...

我也是这样安慰自己的,确实diy成本跟商用成品一样了。
回复

使用道具 举报

194

主题

2947

回帖

9580

积分

元老级技术达人

积分
9580
金钱
6424
HASS币
80
发表于 昨天 16:21 | 显示全部楼层
taida 发表于 2025-11-10 17:13
楼主说的好,这个花了我差不多一张毛爷爷,但是过程很好,反复折腾才是diy的乐趣
...

这个用的是电池方案吧,能用多久,我心动了。颜值不错
回复

使用道具 举报

194

主题

2947

回帖

9580

积分

元老级技术达人

积分
9580
金钱
6424
HASS币
80
发表于 昨天 16:27 | 显示全部楼层
是不是用你这个,替换下,家里平板灯的驱动器就可以了。并且实体开关保持长通电状态就行了
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|Hassbian ( 晋ICP备17001384号-1 )

GMT+8, 2025-11-12 21:41 , Processed in 0.054607 second(s), 8 queries , MemCached On.

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

快速回复 返回顶部 返回列表