pATAq 发表于 2022-3-14 01:48:53

跟着思兼学习Klipper(13) 智能插座与 Gemini 主板[电源管理完结]

## Gemini 等主板比较完美的打完安全关机方案
## 前言
原创文章,转载引用请务必注明链接,水平有限,如有疏漏,欢迎交流指正。

文章如有更新请访问 (https://mc.dfrobot.com.cn/thread-312418-1-1.html?fromuid=725344)。

欢迎对 Klipper 固件感兴趣,以及对改版 CNC 加工的 Voron 三叉戟、v0、v2.4 感兴趣的朋友加群交流(QQ Group:490111638)

我们约定:主板 指 MCU 部分,上位机 指运行 Klippy 的 MPU Linux 部分。

看过前文 [*Klipper 温控风扇以及电源管理(部分)*](https://mc.dfrobot.com.cn/thread-312100-1-1.html?fromuid=725344) 的朋友,一定了解到了安全关闭上位机系统的重要性,当然如果你是平日自家电脑关机靠直接拔电的壮士,下面的文章可以不用看了。

由于 Gemini 二合一主板的 MCU 和 MPU 是不是独立供电的,此外越来越多朋友的 3D 打印机为了好看,选择把上位机和主板一起封在电气箱内,通过总电源开关进行控制,所以就有了如何打完自动 **安全** 关机的必要性。注意,这里的安全是先关闭上位机电脑的 Linux 操作系统,再关闭总电源。

【更新 2022年3月18日】合并自动化规则,添加状态同步规则

## 1、手动安全关机方法的优化 —— 调用 Method

前文介绍过,手动关闭上位机系统有四种方式:物理关机按钮,网页关机按钮、KlipperScreen 彩色触摸屏按钮和 12864 等 LCD 屏幕菜单。

其中`使用 Klipper 监听按钮状态` 和 `mini12864 屏幕菜单` 两种方法都需要 shell_cmd 组件,而此组件的缺点也很明显:

* 需要 Klipper Python2 ,默认不支持中文 Gcode 文件
* shell_cmd 组件不支持需要输入用户密码的命令
* shell_cmd 造成系统安全性下降
* 该组件需要自行安装

那么有没有什么法子不安装额外的东西,即可以实现关闭系统吗?我们上面了解到 Moonraker (下文称 mrk) API 的 `machine.shutdown` 方法,而且 `Fluidd 网页`/`KlipperScreen 按钮` 关机的方法就是创建用户组 `mnrkrsudo、klipperscreensudo` ,执行一些系统命令不需要密码,如下所示:

```shell
fly@flygemini:~$ sudo ls /etc/sudoers.d/
020-sudo-for-moonraker021-sudo-for-klipperscreenREADME
fly@flygemini:~$ sudo cat /etc/sudoers.d/020-sudo-for-moonraker

### Elevate moonraker API rights
### Do NOT allow Command Parts only Full Commands
### for example
###
### /sbin/systemctl "reboot", /sbin/apt "update", .....

Defaults!/usr/bin/apt-get env_keep +="DEBIAN_FRONTEND"

Cmnd_Alias REBOOT = /sbin/shutdown -r now, /bin/systemctl "reboot"
Cmnd_Alias SHUTDOWN = /sbin/shutdown now, /sbin/shutdown -h now, /bin/systemctl "poweroff"
Cmnd_Alias APT = /usr/bin/apt-get
Cmnd_Alias SYSTEMCTL = /bin/systemctl



%mnrkrsudo ALL=(ALL) NOPASSWD: REBOOT, SHUTDOWN, APT, SYSTEMCTL
```

## 1.1Klipper 全家桶 API 介绍

我们常见的有 (https://www.klipper3d.org/API_Server.html)、(https://moonraker.readthedocs.io/en/latest/web_api/) ,具体可以查看链接内文档,而其中的 [*machine.shutdown*](https://moonraker.readthedocs.io/en/latest/web_api/#shutdown-the-operating-system)就是属于 mrk api 。由此 mrk client 可以调用 klippy 提供的 API,同时可以被用户调用其 api。那么 klippy 可以调用 mrk api 来实现执行关机命令吗?也是可以的,还记得之前我们从 Klipper 内关闭打印机电源的示例吗,其有一个命令:`action_call_remote_method` ,查看 (https://www.klipper3d.org/Command_Templates.html?h=action#actions),是这么说的:

> - `action_call_remote_method(method_name)`: ***Calls a method registered by a remote client.*** If the method takes parameters they should be provided via keyword arguments, ie: `action_call_remote_method("print_stuff", my_arg="hello_world")`

!(https://cdn.jsdelivr.net/gh/sjqlwy/blog_imgs@default/images/20220313215837.png)

意思就是除了上面两种 API 之外,Moonraker 还可以在 Klippy 那注册一个方法,告诉它(Klippy):我(Mrk)这里有此方法,可以被调用。

!(https://cdn.jsdelivr.net/gh/sjqlwy/blog_imgs@default/images/20220313222554.png)

我们以已知方法(method)`set_device_power` 为关键词搜索 mrk 源码,可知关键词为 `register_remote_method` ,再以此搜索即获得支持的方法如下:

* moonraker_test
* **shutdown_machine**
* **reboot_machine**
* process_gcode_response
* process_status_update
* pause_job_queue
* start_job_queue
* set_wled_state
* set_wled
* publish_mqtt_topic
* paneldue_beep
* set_device_power

至此我们找到了想要的两个可以被 Klipper 调用的方法。

> **【更新:2022年3月13日】**
>
> 笔者测试时还没有相关文档,(https://github.com/Arksine/moonraker/commit/65e76aeec8c81481fdd587b046504d63dff52870) 官方更新了 [此部分文档](https://moonraker.readthedocs.io/en/latest/configuration/#machine) ,并补充了注意事项,建议阅读。虽然如此,我们查找答案的方法还是有记录借鉴意义的。

## 1.2 调用 `Method` 实现关机

### 物理关机按钮:

```yaml
# ~/klipper_config/printer.cfg


gcode:
{action_call_remote_method("shutdown_machine")}

# 定义物理按钮 【监听引脚状态】

# 按钮信号连接引脚
pin: host:gpio

# 按下时执行 gcode 宏命令
#press_gcode:

# 松开时执行 gcode 宏命令
release_gcode:
SHUTDOWN

```

测试通过,控制台输入 `SHUTDOWN` 或者被调用即可以实现关闭系统指令。

### 12864 屏幕菜单关机:

```yaml
# printer.cfg
### menu poweroff by 思兼 v2 ###


type: list
enable: {not printer.idle_timeout.state == "Printing"}
name: PowerOff


type: command
name: NO
enable: {not printer.idle_timeout.state == "Printing"}
gcode:
{menu.back()}


type: command
name: YES
enable: {not printer.idle_timeout.state == "Printing"}
gcode:
SHUTDOWN
```

## 2、 传统打完关机方案

我们先说说传统的打完关机功能,适用于 **【上位机独立供电的情况】**,多数朋友借助原来 Marlin 上用的 `打完关机模块/继电器`,或者使用 `智能插座`,我们说说各自的优缺点:

!(https://cdn.jsdelivr.net/gh/sjqlwy/blog_imgs@default/images/20220313225817.png)

## 2.1 打完关机模块

### 优点:

* 可以利用原来的模块不浪费
* 支持断电检测

### 缺点:

* 接线麻烦
* 价格较高 (79 + 9元)
* 为了安全关闭上位机系统,需要上位机独立供电:
* 如果由上位机 GPIO 引脚控制,支持开、关机操作,上传打印任务后自动开机
* 如果由主板引脚控制,仅支持关机,且要求主板支持从上位机取电,但此时会造成 5v 和 VCC 混合供电,导致潜在的信号干扰

示例如下:

```yaml
# 上位机独立供电打完关机方案,by 思兼

#####################################################################
#方法1,打印机主板 PS_ON 接口
#####################################################################
# 使用主板 PS_ON 引脚,需要上位机通过 USB 接口为主板供电,但此时会出现 5v
# 和 12/24v 同时供电的问题


pin: PS_ON
pwm: False
value: 1
shutdown_value: 0


description: PowerOff
gcode:
M84
SET_PIN PIN=mks_pwc VALUE=0

#####################################################################
#方法2,上位机 GPIO 接口【推荐】
#####################################################################
# 使用上位机 GPIO 接口控制,支持开/关机,尤其适合上位机内置。需要额外修改
# moonraker.cfg 配置文件,可以手动在右上角菜单控制开关机


gcode:
{action_call_remote_method("set_device_power",
                           device="printer",
                           state="off")}

#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
# 以下为修改 moonraker.cfg内容,“printer”名称对应。以及不能使用 UART 等引脚

type: gpio
pin: gpio26
off_when_shutdown: True
initial_state: off

#####################################################################
#延时(挤出头降温)自动关机
#####################################################################

# 判断当前为空闲状态,才执行断电操作
initial_duration: 0.
gcode:
{% if printer.idle_timeout.state == "Idle" %}
    M81
{% endif %}


# https://www.klipper3d.org/Command_Templates.html?h=update_delayed_gcode#delayed-gcodes
# https://github.com/Klipper3d/klipper/issues/3865 | Firmware Restart 之后,要先home之后才会进入空闲状态
# 空闲状态360s后关闭电机和加热头,60s后关闭电源
gcode:
M84
TURN_OFF_HEATERS
UPDATE_DELAYED_GCODE ID=delayed_printer_off DURATION=60
timeout: 360
```

## 2.2 智能插座

### 优点:

* 价格相对便宜(30~50元)
* 无需额外接线
* 支持电量统计(可选)

### 缺点:

使用方法有以下几种,各有其不足:

1. 预估打印完成时间定时关机 —— 不准确
2. 使用具有电量检测功能的插座,当打印机处于低耗电时,认为打印完成,实现关机 —— 相对准确,成本提高
3. 使用 Home Assistant 等智能家居系统,Klipper 打印完成后,调用 Moonraker 向 HA 发送关闭智能插座命令 —— 准确,但是需要 HA

其他用法欢迎补充。前两种方法自己从 APP 里设置即可,第三种方法我们下面一起介绍。

## 3、Gemini 等主板打完自动安全关机方案

此类主板的问题就是上位机和主板供电一起的,无论使用 `打完关机模块` 还是 `智能插座`:

* **先断电** | 导致系统意外断电关机
* **先关闭系统** | 导致模块无法收到断电指令

我们分析一下理想的关机断电方案(横轴为时间):

!(https://cdn.jsdelivr.net/gh/sjqlwy/blog_imgs@default/images/20220313233837.png)

之前想过用 `延时继电器` (加自锁?)实现,但是想想还是智能插座更方便。同样有三种方案:

## 3.1 TPLink 智能插座

无需烧录固件和搭建 HA 系统,可以直接被 mrk 调用,但是 (https://moonraker.readthedocs.io/en/latest/configuration/#tplink-smartplug-configuration) 之后有无法在局域网使用的可能。

## 3.2 智能插座烧录 Tasmota 固件

优缺点:

* 不需要家里有 HA 智能家居系统
* 需要拆开智能插座烧录固件,需要额外的工具,且有破拆外壳风险
* 仅支持 ESP8266/ESP32 等方案的智能插座,实测 ESP8266 烧录成功,ESP32 不行,需要进一步测试。

!(https://cdn.jsdelivr.net/gh/sjqlwy/blog_imgs@default/images/20220313235115.png)

目前了解到的是 `gosund` 和 `全橙` 的部分智能插座使用 ESP8266 芯片。tasmota 目前支持的设备列表可以在 [这里](https://templates.blakadder.com/) 查看。

### 3.2.1 为 ESP8266 设备烧录 Tasmota 固件

#### 烧录软件:

* [**ESP-Flasher**](https://github.com/Jason2866/ESP_Flasher) \- GUI flasher for Tasmota based on esptool.py for ESP82XX *and* ESP32. (Windows or Mac)
* [**Tasmotizer**](https://github.com/tasmota/tasmotizer) - flashing and firmware download tool *for ESP82XX only*. (Windows, Linux or Mac) | 只支持 ESP8266
* [**Tasmota Web Installer**](https://tasmota.github.io/install/) - flash Tasmota using a Chrome based browser for ESP82XX and ESP32 | 在线烧录页面

任选其一即可。

!(https://cdn.jsdelivr.net/gh/sjqlwy/blog_imgs@default/images/20220314000552.png)

#### 下载固件:

* 固件下载地址:https://tasmota.github.io/docs/Download/
* 点击 `Current release` ,选择 (http://ota.tasmota.com/tasmota/release/tasmota-CN.bin)

#### 烧录固件:

有的 ESP8266 模块自带串口芯片(如 NodeMCU),如果没有(如 Weduino)则自备 USB串口模块进行烧录。ESP8266 模块仅支持 3.3v,务必不要接错,建议独立供电,不建议由串口模块供电,烧录时 `GPIO0` 置于低电平进入烧写模式,借用一下老图。

!(http://ww1.sinaimg.cn/mw690/90fd3da6gy1fctldvibwwj21kw2t51l0)

!(http://ww1.sinaimg.cn/mw690/90fd3da6gy1fctogrg667j20pb0f6wi9)

#### 连接设置 Tasmota

烧录完成后,重新上电稍等片刻可以看到一个 `tasmota_XXXXXX-####` 的热点,连接上去并打开网址:`http://192.168.4.1` 即可进入 Tasmota 设置界面。

!(https://tasmota.github.io/docs/_media/wificonfig1.jpg)

**注意:**

* 热点只会存在 3分钟,需要重启模块才会再次出现。
* 新界面会提示你输入当前无线网络的用户名和密码,注意是 2.4GHz 的,ESP8266 不支持 5GHz 无线网络。
* 连接无线网络后分配的地址可以在路由器管理页面内或者使用 `Fing` 等局域网扫描软件查看。

进入后可以进行一些设置,这里我就不演示了。

### 3.2.2 设置 Moonraker

```yaml
# moonraker.cfg


type: tasmota
address: 192.168.0.201 # 请设置正确的设备 IP 地址
initial_state: on
off_when_shutdown: True
timer: 10            # 请设置合适的延时时间,单位 秒
```

注意这里一个官方文档里没有提到的隐藏参数:`timer` ,可以实现发送断电命令后,插座延时断电的功能。具体参考 [***power.py add parameter timer***](https://github.com/Arksine/moonraker/pull/139) 和源码。注意,这里的 `timer` 和 (https://moonraker.readthedocs.io/en/latest/configuration/#gpio-device-configuration) 里的作用不一样:

>   A time (in seconds) after which the device will power off after being.switched on. This effectively turns the device into amomentary switch.This option is available for gpio, klipper_device, tplink_smartplug,shelly, and tasmota devices.The timer may be a floating point valuefor gpio types, it should be an integer for all other types.Thedefault is no timer is set.

后者实现的是类似开启后延时自动断开的功能,无法在触发条件后才断开。

!(https://cdn.jsdelivr.net/gh/sjqlwy/blog_imgs@default/images/20220314004756.png)

* 灯珠发绿色光为开启状态,蓝色为断开状态
* 设置断开后,延时 5s 才断开

## 3.3 使用 Home Assistant 控制智能插座

优缺点:

* 需要 HA 智能家居系统,如果之前家里就有就很方便
* 兼容性强,支持多种智能插座,大多数天猫精灵、小爱同学的都支持
* 不需要额外烧录固件,不需要拆开智能插座

整体来说,此方案更优秀。

### 3.3.1 搭建 Home Assistant 系统

方法众多,此处略去不表。我使用 树莓派4 + [中文镜像](https://www.hachina.io/hassos_hachina_img/) 进行测试。后续会在家里的软路由上进行安装,感觉树莓派镜像还挺麻烦的,由于国内网络不好导致安装体验差。[这个教程](https://pimylifeup.com/home-assistant-raspberry-pi/) 也有帮助。

### 3.3.2 添加智能插座设备

这里我以天猫精灵赠送的 `BroadLink SP mini 3` 为例:

!(https://cdn.jsdelivr.net/gh/sjqlwy/blog_imgs@default/images/20220314005451.png)

#### 添加智能插座

1. 下载 `博联智能` APP,在其中可以连接配置此智能插座并看到设备 MAC 地址。同时点击右上角 `...` 图标—— `属性` —— 关闭 `设备上锁`。同时根据 MAC 地址确认设备 IP 地址。

2. 打开 http://homeassistant.local:8123/ 管理页面,选择 `配置` —— `设备与服务` —— `添加集成`(右下角)—— `Broadlink`,输入设备 IP 即可连接。

!(https://cdn.jsdelivr.net/gh/sjqlwy/blog_imgs@default/images/20220314005702.png)

!(https://cdn.jsdelivr.net/gh/sjqlwy/blog_imgs@default/images/20220314010617.png)

如是即可在 HA 主面板控制智能插座开关了。

#### 延时关闭智能插座思路

由于我也是半路出家,第一次用 HA,网上搜到的都是开启后定时自动关闭,不是我想要的功能。思索一番,能想到的延时方法就是创建一个中间开关实体命名为 `Relay`,再为它创建一个自动化规则,当 Moonraker 发送关闭命令后:

* Condition:Relay 从 on 到 off
* Action:延时 10s,关闭智能插座

#### 创建 HA 辅助元素

!(https://cdn.jsdelivr.net/gh/sjqlwy/blog_imgs@default/images/20220314011215.png)

!(https://cdn.jsdelivr.net/gh/sjqlwy/blog_imgs@default/images/20220314011418.png)

* 开关名称随便起,这里是 `Gemini_Relay`
* 图标随便选

#### 创建自动化规则

同样界面选择 `自动化` 标签 —— `创建自动化` —— `从空的自动化开始`,右上角选择 `以 YAML 编辑`,你也可以事后查看图形化过程。

!(https://cdn.jsdelivr.net/gh/sjqlwy/blog_imgs@default/images/20220314011847.png)

输入以下内容:

```yaml
alias: 延时关闭插座
description: '收到 mrk 关闭指令,延时关闭智能插座'
trigger:
- platform: state
    entity_id: input_boolean.gemini_relay
    from: 'on'
    to: 'off'
condition: []
action:
- delay:
      hours: 0
      minutes: 0
      seconds: 10
      milliseconds: 0
- type: turn_off
    device_id: cbd09a4013a7cba034a1e42090227354
    entity_id: switch.wi_fizhi_neng_cha_zuo_switch
    domain: switch
mode: single

```

注意 `device_id` 和 `entity_id` 可以从图形界面直接选择。对应的正常打开智能插座配置如下,虽然用不上:

```yaml
alias: 正常打开智能插座
description: ''
trigger:
- platform: state
    entity_id: input_boolean.gemini_relay
    from: 'off'
    to: 'on'
condition: []
action:
- type: turn_on
    device_id: cbd09a4013a7cba034a1e42090227354
    entity_id: switch.wi_fizhi_neng_cha_zuo_switch
    domain: switch
mode: single
```

【V2】

```yaml
alias: 延时关闭智能插座
description: 收到 mrk 关闭指令,延时关闭智能插座。正常打开插座
trigger:
- platform: state
    entity_id: input_boolean.gemini_relay
condition: []
action:
- choose:
      - conditions:
          - condition: state
            entity_id: input_boolean.gemini_relay
            state: 'on'
      sequence:
          - type: turn_on
            device_id: cbd09a4013a7cba034a1e42090227354
            entity_id: switch.wi_fizhi_neng_cha_zuo_switch
            domain: switch
      - conditions:
          - condition: state
            entity_id: input_boolean.gemini_relay
            state: 'off'
      sequence:
          - delay:
            hours: 0
            minutes: 0
            seconds: 15
            milliseconds: 0
          - type: turn_off
            device_id: cbd09a4013a7cba034a1e42090227354
            entity_id: switch.wi_fizhi_neng_cha_zuo_switch
            domain: switch
    default: []
mode: single

```



再创建一个同步插座状态的规则,因为有时候会手动通过插座按钮关闭。

```yaml
alias: 同步智能插座状态
description: ''
trigger:
- platform: device
    type: changed_states
    device_id: cbd09a4013a7cba034a1e42090227354
    entity_id: switch.wi_fizhi_neng_cha_zuo_switch
    domain: switch
condition: []
action:
- choose:
      - conditions:
          - condition: device
            type: is_on
            device_id: cbd09a4013a7cba034a1e42090227354
            entity_id: switch.wi_fizhi_neng_cha_zuo_switch
            domain: switch
      sequence:
          - service: input_boolean.turn_on
            data: {}
            target:
            entity_id: input_boolean.gemini_relay
      - conditions:
          - condition: device
            type: is_off
            device_id: cbd09a4013a7cba034a1e42090227354
            entity_id: switch.wi_fizhi_neng_cha_zuo_switch
            domain: switch
      sequence:
          - service: input_boolean.turn_off
            data: {}
            target:
            entity_id: input_boolean.gemini_relay
    default: []
mode: single

```



!(https://cdn.jsdelivr.net/gh/sjqlwy/blog_imgs@default/images/20220314012744.png)

#### 创建 HA 长期访问令牌

!(https://cdn.jsdelivr.net/gh/sjqlwy/blog_imgs@default/images/20220314013152.png)

**注意:**

* 此令牌无法再次查看,请妥善保存
* **此令牌非常长**,复制的时候注意复制完全

#### 创建 Moonraker 配置

想要控制 HA 设备,需要用到上面 HA 创建的令牌(Token),官方给的 (https://moonraker.readthedocs.io/en/latest/configuration/#home-assistant-configuration-http) 直接控制智能插座,无法实现延时功能。我们照葫芦画瓢整一个:

```yaml
# moonraker.cfg

type: homeassistant
address: 【HA IP】
port: 8123
device:input_boolean.Gemini_Relay
domain: input_boolean
token: 【TOKEN】

```

**注意:**

* 请设置为你 HA 所在的设备 IP 地址
* 请设置为上面创建的令牌
*`homeassistant_switch` | 随便起,支持中文例如 `智能插座`
* `Gemini_Relay` | 请改为你创建的实体名称

!(https://cdn.jsdelivr.net/gh/sjqlwy/blog_imgs@default/images/20220314014036.png)

至此,我们的智能插座通过一个虚拟中继,比较完美的实现了 Gemini 之类的主板安全关机,同时我关于 Klipper 电源管理方面的文章也告一段落。

QQQQQQQ 发表于 2022-3-14 19:02:59

厉害厉害

赤星三春牛! 发表于 2022-3-17 23:15:15

厉害厉害

赤星三春牛! 发表于 2022-3-17 23:16:17

66666666666

赤星三春牛! 发表于 2022-3-17 23:17:39

呵呵呵呵

赤星三春牛! 发表于 2022-3-17 23:19:12

赞赞赞赞赞

赤星三春牛! 发表于 2022-3-17 23:20:48

哈哈哈哈哈哈

destinia 发表于 2022-8-24 14:23:11

创建好了Moonraker配置,手动点击可以实现延时断电了,但是怎么让它打完自动触发?需要gcode添加什么代码吗?

pATAq 发表于 2022-8-24 17:13:32

destinia 发表于 2022-8-24 14:23
创建好了Moonraker配置,手动点击可以实现延时断电了,但是怎么让它打完自动触发?需要gcode添加什么代码吗 ...

可以参考我的几篇文章,所有相关内容都有

熔融三维 发表于 2023-2-4 22:48:09

跟着思兼大佬学到很多东西

简醉猫 发表于 2023-4-18 19:14:30

根据教程完成了HA的自动关机,谢谢大佬
页: [1]
查看完整版本: 跟着思兼学习Klipper(13) 智能插座与 Gemini 主板[电源管理完结]