pATAq 发表于 2023-1-28 05:32:31

跟着思兼学习Klipper(19)通知系统:蜂鸣器、语音与手机信息

## 跟着思兼学习Klipper(19) 通知系统:蜂鸣器、语音播报与手机消息通知
## 前言
原创文章,转载引用请务必注明链接,水平有限,如有疏漏,欢迎指正交流。

本文目的在于交流,其中原创内容禁止一切非授权商业行为。

文章如有更新请访问 (https://mc.dfrobot.com.cn/thread-315290-1-1.html?fromuid=725344) 或者 (https://www.cnblogs.com/sjqlwy/p/klipper_notify.html)。

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

今天总结下 Klipper 的通知系统,介绍三个,一是使用蜂鸣器,二是使用上位机上的扬声器,三是手机信息通知。常用的调用包括开始打印、结束打印、断料检测等。

## 1、蜂鸣器播放提示音

![](https://gd1.alicdn.com/imgextra/i1/12778391/TB29hOYJ25TBuNjSspcXXbnGFXa_!!12778391.jpg)

【屏幕上的蜂鸣器】

蜂鸣器一般分为两种:有源和无源。主要区别是前者通电后固定频率发出声音,后者频率可变,因此可以演奏旋律。一般 LCD 显示屏上的蜂鸣器都是无源的。

> 无源蜂鸣器是没有正负之分的,类似于喇叭,只要在两个腿上加载不同的频率的电信号就可以实现发声,根据不同的频率所发出的声音也是不一样的。 有源蜂鸣器是有正负之分的,只需要在两个腿上加上电压信号就会发声,发出的声音音调单一、频率固定。 有源蜂鸣器比无源蜂鸣器内部多了振荡结构,所以有源蜂鸣器在价格上稍微贵一点。Source:(https://www.sohu.com/a/78949147_399839)

Gcode 中有一个命令 M300,用于播放提示音,但是 Klipper 默认没有实装,所以我们可以自己写一个宏,以下宏来源记不清了,如有知道的欢迎补充。

```shell
######################################################################
# Beeper | 蜂鸣器设置
######################################################################

# M300 : 播放提示音,支持常见的无源蜂鸣器。使用方法:
#   M300
#   P 为持续时间, S 为频率.
# 需要注意的是频率和音高并不是完美对应.

## 定义蜂鸣器控制引脚,一般使用 LCD 上的 EXP1_1 板载蜂鸣器,也可以使用外置蜂鸣器接到 GPIO 引脚。

pin: EXP1_1
# 无源蜂鸣器(piezo beeper)需要 PWM 信号控制,有源(DC buzzer)不需要
pwm: True
value: 0
#   Silent at power on, set to 1 if active low.
shutdown_value: 0
#   Disable at emergency shutdown (no PWM would be available anyway).
cycle_time: 0.001
#   Default PWM frequency : 0.001 = 1ms will give a tone of 1kHz
#   Although not pitch perfect.

## 定义 M300 宏,实现 Gcode 功能

description: 播放提示音
gcode:
    # Use a default 1kHz tone if S is omitted.
    {% set S = params.S|default(1000)|int %}
    # Use a 10ms duration is P is omitted.
    {% set P = params.P|default(100)|int %}
    SET_PIN PIN=BEEPER_pin VALUE=0.5 CYCLE_TIME={ 1.0/S if S > 0 else 1 }
    G4 P{P}
    SET_PIN PIN=BEEPER_pin VALUE=0
   
######################################################################
# 开始、结束打印时调用 M300 播放提示音,示例来自 CHEP
######################################################################


gcode:
    # chirp to indicate starting to print
    M300 S1000 P500
    # Use absolute coordinates
    G90
    # Reset the G-Code Z offset (adjust Z offset if needed)
    SET_GCODE_OFFSET Z=0.0
    # Home the printer
    G28
    # G1 X10 Y50 Z0.2 F3000
    # 避开夹子
    G1 Y60 F3000
    G1 X10 Z0.2 F3000
    G92 E0
    G1 Y130 E10 F225
    G92 E0


gcode:
    # Turn off bed, extruder, and fan
    M140 S0
    M104 S0
    M106 S0
    # Move nozzle away from print while retracting
    G91
    G1 X-2 Y-2 E-3 F300
    # Raise nozzle by 10mm
    G1 Z10 F3000
    G90
    G28 X Y
    # Disable steppers
    M84
    # 播放结束提示音
    M300 S440 P200
    M300 S660 P250
    M300 S880 P300
```
以上除了最后的调用示例,其他的不需要修改(必要时修改蜂鸣器控制引脚)。一般我习惯放在 `display.cfg` 里面进行调用。

当然,你也可以生成并播放复杂的旋律,或者打印翻车的时候播放 `120` 的提示音

## 2、扬声器语音播报

之前在群里讨论过语音播报,什么重新上料后 `哥哥不行了,已经插到底了` 、开始打印 `啊~~ 要开始了吗`、打印结束 `哥哥我还要,再来一次吧`,云云。这次借着 KlipperPad 上的俩喇叭,来试验一下。当然你也可以使用外置扬声器(HDMI、3.5mm),记得选择正确的音频输出设备,具体可以参考 Archwiki。使用扬声器播报主要包括以下几点:

### 2.1 语音生成

这里可以使用在线免费服务:(https://ttsmaker.com/),测试了众多服务之后,这个最简单,也可以使用 Microsoft、阿里、讯飞等的 API。

### 2.2 命令行播放音频文件

首先要选择合适的终端命令行音频文件播放程序,KlipperPad 预装了 VLC 多媒体播放软件,可以直接调用其命令行版本 `cvlc`,对于其他系统,可以考虑 `mpg123` ,软件依赖少。

测试后发现有三种情况:

* 桌面环境下的 shell
* SSH 登录的 shell,直接调用播放有延迟,可以指定 `DISPLAY=:0` 来解决,序号根据实际情况填写,一般是 0 。
* Klipper 调用的 shell

### 2.3 创建并调用语音播报宏

这里借助 shell_cmd 组件,注意它带来了极大的便利,方便与系统交互,但是由于会使能调用 shell 脚本程序,带来可能的安全性隐患,不建议暴露到公网使用。

```shell
## 创建播放语音的 shell 脚本
mkdir ~/kbox_scripts
mkdir ~/printer_data/config/misc
cat << '_EOF_' > /home/pi/kbox_scripts/voice_notify.sh
!/bin/bash

case $1 in
    0)sound="print_start"
    ;;
    1)sound="print_end"
    ;;
    2)sound="runout"
    ;;
    *)echo 'You do not input a valid number between 0 to 3'
    ;;
esac
【回复后可见】
_EOF_

## 创建 Klipper 宏脚本
cat << _EOF_ > /home/pi/printer_data/config/misc/voice_notify.cfg



command: bash /home/pi/kbox_scripts/voice_notify.sh
timeout: 5.
verbose: False


description: 语音播报功能, 后期将采用林志玲语音,调用命令:voice_notify INFO=0 | by 思兼
gcode:
    {% if params.INFO is not defined %}
      RESPOND MSG='请正确填写语音编号: 0-打印开始, 1-打印结束, 2-检测到断料'
    {% else %}
      RUN_SHELL_COMMAND CMD=voice_notify PARAMS={params.INFO}
    {% endif %}
_EOF_
```

**测试及使用方法:**

* 目前 KlipperPad 内置支持 `打印开始`、`打印结束`、`检测到断料` 三个语音,位于 `~/Music` 文件夹内,分别对应数字 0-1-2 ,后续可根据需求增加。
* **启用语音播报:** 在 `printer.cfg` 文件中增加一句:`` 即可
* **调用方法:** 以打印开始为例,INFO后面的数字对应语音,在对应的功能处添加一行 Gcode:`voice_notify INFO=0`。也可以手动在网页控制台中输入上述指令进行测试。

## 3、手机消息通知

触发特定事件后给手机发送通知的功能在生活中用处很多,比如流行的 IFTTT(If This Then That) 服务。我常用的国内免费且使用方便的通知服务包括:

* 绑定微信的 QQ 邮箱

* 使用电信手机的 189 邮箱,自带短信提醒

* Server酱,不过改版后没以前方便了,增加了使用限额

如果想在 Klipper 系统中使用的话,可以看看 Fly3D 的关于微信通知的视频。不过对于普通用户,可以看看 Moonraker 内置的 apprise 服务,目前支持飞书、钉钉、微信、短信、邮箱等等,部分支持多媒体文件,详细支持列表可以参考 ***(https://moonraker.readthedocs.io/en/latest/configuration/#notifier)***。

### 3.1 选择合适的通知服务

对我而言,理想的通知服务最好有以下特征:

* 无需注册,使用简单
* 软件功能简洁,不要动辄几百兆的臃肿软件
* 支持多媒体文件
* 免费使用或者可以自行搭建服务

如果与现有日常软件结合,比较理想的就是飞书、钉钉、微信等。但是多少都无法满足所有条件。搜索、研究、试用一番,***(https://ntfy.sh/)*** 是一个比较易用的服务。

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

### 3.2 安装测试 ntfy

此服务拥有 Android、iOS 客户端,还支持网页端,非常方便,可以从 ***[此处](https://docs.ntfy.sh/subscribe/phone/)*** 下载。

安装完毕后,我们先打开 ***[网页端](https://ntfy.sh/app)***和 Android 客户端,首先需要订阅/添加一个主题,并发送一条测试信息,是不是很简单?无需注册,免费使用。当然,此主题目前没有身份验证,所有互联网用户订阅了此主题都可以收到信息,所以我们要进一步设置。

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

### 3.3 moonraker 配置 ntfy

我们参考文档,创建一个 ntfy 实例即可使用。

```yaml
# moonraker.conf


url: ntfy://{topics}
#   The url for your notifier. This URL accepts Jinja2 templates, so you can use if you want.
events: *
#   The events this notifier should trigger to. '*' means all events.
#   You can use multiple events, comma separated.
#   Valid events:
#      started
#      complete
#      error
#      cancelled
#      paused
#      resumed
body: "打印机状态已变为: {event_name}"
#   The body of the notification. This option accepts Jinja2 templates.
#   You can use {event_name} to print the current event trigger name. And {event_args} for
#   the arguments that came with it. When using the notify functionality in a macro context, you can
#   use {event_message} to print out your message.
title: Klipper 提醒,love from china
#   The optional title of the notification. Just as the body, this option accepts Jinja2 templates.
attach: http:///webcam/?action=snapshot
#   An optional attachment. Can be an url of a webcam for example. Note: this isn't available for all
#   notification services. You can check if it's supported on the Apprise Wiki. Be aware that links in
#   your internal network can only be viewed within your network.
```

### 3.4 自定义事件调用 ntfy

上述支持 moonraker 内置事件,如果想自定义事件,则包括自定义提示器和 Klipper 调用宏。

```yaml
## moonraker.conf

url: ntfy://klipper_test
events: gcode
# 事件消息接收自 Klippy
body: {event_message}
attach: http:///webcam/?action=snapshot

## printer.cfg

gcode:
{action_call_remote_method("notify",
                           name="runout",
                           message="Filament Runout!")}
```

理想很丰满,现实很骨感,摄像头画面并没有如期出现,并抛给你一个错误。

### 3.5 慢慢排障路

moonraker.log 报错如下,提示附件位置无效:` - Attachment of notifier 'runout' is not valid. The location of the attachment is not accessible.` 结果是上传了一个文件,拓展名为 bin 文件,无法作为 jpg 打开。

目前用到的 ntfy 调用方式从上到下为 moonraker —— apprise —— ntfy,循序排查之。

#### apprise 调试信息

以如下命令调用 apprise,信息如下:

```shell
python3 -m pip install apprise
/home/pi/.local/bin/apprise -vv -t "测试标题" -b "测试内容" "ntfy://klipper_test/?attach=/home/pi/shot.jpg"
# WARNING - Failed to send ntfy notification to topic 'klipper_test': invalid request: attachment URL is invalid, error=40013.

/home/pi/.local/bin/apprise -vv -t "测试标题" -b "测试内容" 'ntfy://klipper_test/?attach=http://192.168.0.204/webcam/?action=snapshot&filename=smoke.jpg'
# 此命令正确显示图像,且更改名称,但是仅仅是 url 链接,导致仅内网可访问,并未上传到 ntfy 服务器。记得是这样,注意参数调用的双引号。
# ntfy 支持参数 https://github.com/caronc/apprise/wiki/Notify_ntfy#parameter-breakdown

/home/pi/.local/bin/apprise -vv -t "测试标题" -b "测试内容" 'ntfy://klipper_test' --attach=http://192.168.0.204/webcam/?action=snapshot
# 此命令可以上传文件,但是变成 bin 文件,无法作为图像打开
```

显然上述无论是上传成为无法打开的 bin 文件,还是没有上传只能局域网显示的 URL 链接,都不是我们想要的结果。

**小结:**

> /?attach 仅支持url,不会上传。shot.jpg 或者 snapshot 结尾的 url 都可以
> --attach 支持本地文件和url,会上传文件,但是图片会bin,文本正常

#### ntfy curl 调试

我们再试试 ntfy 官方提供的上传方法:

```shell
curl \
    -T /home/pi/shot.jpg \
    -H "Filename: flower.jpg" \
    ntfy.sh/klipper_test
   
curl \
    -X POST \
    -H "Attach: http://192.168.0.240/webcam/?action=snapshot" \
    ntfy.sh/klipper_test
   
```

结果前者正常上传、显示。其中后者并不上传,仅作引用。

#### 查看 ntfy apprise 源码

* ntfy 的调用方法可以查看 (https://kgithub.com/caronc/apprise/blob/7c993fb7f692d0cce7a0a9f8b6d098f9a2e136d8/test/test_plugin_ntfy.py) 和 (https://github.com/caronc/apprise/blob/master/apprise/plugins/NotifyNtfy.py) 两个文件。

* ntfy 官方的 [上传本地文件 Python 示例](https://docs.ntfy.sh/publish/#attach-local-file)

```python
requests.put("https://ntfy.sh/flowers",
      data=open("flower.jpg", 'rb'),
      headers={ "Filename": "flower.jpg" })
```

* apprise 的上传文件 `--attach` 示例(NotifyNtfy.py)

```shell
r = requests.post(
      notify_url,
      params=params if params else None,
      data=dumps(data) if data else None,
      headers=headers,
      files=files,
      auth=auth,
      verify=self.verify_certificate,
      timeout=self.request_timeout,
)
```

分析如下:

1. 都使用 requests 库

2. 一个使用 put,一个使用 post

3. 后者使用 json 传输数据

4. 前者使用 data 传输图片,后者使用 files


最终怀疑问题出在 4 上可能性大,写一个测试程序:

```python
# python3
import requests
url = 'https://ntfy.sh/klipper_test'
files = {'file': open('/home/pi/shot.jpg', 'rb')}
data = open(attach.path, 'rb'),
headers={ "Filename": "flower.jpg" }
r = requests.post(url, files=files, headers=headers)
r = requests.put(url, data=data, headers=headers)
```

确认上传后 jpg 图像变成 bin 的元凶就是 `files`。

查看 (https://cn.python-requests.org/zh_CN/latest/user/quickstart.html),关于 (https://cn.python-requests.org/zh_CN/latest/user/quickstart.html#post-multipart-encoded):

```python
# Requests 使得上传多部分编码文件变得很简单:
files = {'file': open('report.xls', 'rb')}
# 你可以显式地设置文件名,文件类型和请求头:
files = {'file': ('report.xls', open('report.xls', 'rb'), 'application/vnd.ms-excel', {'Expires': '0'})}
```

指定为 `image/jpeg` 仍然无效。临时解决方案:

```shell
find ~ -name 'NotifyNtfy.py'

/home/pi/.local/lib/python3.9/site-packages/apprise/plugins/NotifyNtfy.py
/home/pi/moonraker-env/lib/python3.9/site-packages/apprise/plugins/NotifyNtfy.py

r = requests.post(
    notify_url,
    params=params if params else None,
    #data=dumps(data) if data else None,
    data=open(attach.path, 'rb'),
    headers=headers,
    #files=files,
    auth=auth,
    verify=self.verify_certificate,
    timeout=self.request_timeout,
)

/home/pi/.local/bin/apprise -vv -t "测试标题" -b "测试内容" 'ntfy://klipper_test' --attach=/home/pi/shot.jpg --attach=http://192.168.0.142:8080/shot.jpg
```

测试成功上传、显示。上传多个文件时,会显示多条信息。但是 `/?atatch=` 参数从此报错,此情景暂时不用,后续完善。

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

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

### 3.6 杂项

* 中间有一种思路怀疑摄像头快照地址需要是 `jpg` 结尾,于是试了 `ip cam`,无效。

* 手动抓取快照地址保存为 `jpg` ,或者用 (https://github.com/mainsail-crew/moonraker-timelapse) 插件,不行。

* ntfy 支持自行部署,官方服务器不支持身份验证,所以所有人都可以订阅你的频道查看你的信息,除非使用自建服务器。

> By default, the ntfy server is open for everyone, meaning **everyone can read and write to any topic** (this is how ntfy.sh is configured). To restrict access to your own server, you can optionally configure authentication and authorization.

### 3.7 其他功能(待续)

* KlipperBoxOS 用户发送日志、配置

* 开机联网后自动发送设备IP地址:systemd、curl,nm,example

三春牛-创客 发表于 2023-1-28 22:22:00

厉害厉害

花生编程 发表于 2023-1-28 22:33:40

厉害,赞!

LIHZ 发表于 2023-4-22 17:22:30

xuexiyixia

LIHZ 发表于 2023-4-22 19:03:40

设置后提示:
Section 'gcode_shell_command voice_notify' is not a valid config section

Once the underlying issue is corrected, use the "RESTART"
command to reload the config and restart the host software.
Printer is halted

pATAq 发表于 2023-4-29 14:05:01

LIHZ 发表于 2023-4-22 19:03
设置后提示:
Section 'gcode_shell_command voice_notify' is not a valid config section



需要使用shell_cmd组件
页: [1]
查看完整版本: 跟着思兼学习Klipper(19)通知系统:蜂鸣器、语音与手机信息