跟着思兼学习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 厉害厉害 厉害,赞! xuexiyixia 设置后提示:
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 LIHZ 发表于 2023-4-22 19:03
设置后提示:
Section 'gcode_shell_command voice_notify' is not a valid config section
需要使用shell_cmd组件
页:
[1]