基于Arduino Nano的看门狗

看门狗是一种旨在检测硬件故障并对其进行故障排除的设备。通常,为此使用计时器,计时器的定期重新启动会阻止信号发送到重新启动。



我主要将Gentoo上的主服务器用于实验,但是,它运行着许多服务,如果可能的话,应该可以不中断地使用它们。不幸的是,某些实验的结果在最不合时宜的时刻导致内核崩溃,100%CPU负载和其他麻烦。因此添加看门狗的想法长期以来一直受到关注,并最终在该设备中得以实现。

在仔细检查可用内容并评估可用时间之后,基于Arduino Nano组装的看门狗是最佳选择。需求清单大致相同:

  1. 启动和停止守护程序以使用计时器(常规OS工具(OpenRC))工作。
  2. 设备上有自己的看门狗,在ATmega中,您需要使用它。
  3. 设备上用于修复重启和计时器的事件日志。
  4. 将设备的时间与主机同步,以在日志中记录正确的时间。
  5. 接收并显示设备及其日志条目的状态。
  6. 清除日志并将设备重置为原始状态。

因此,找到了“显微镜”,在“指甲”上做了标记……您可以锤击。

硬件


该设备的基础是基于CH340芯片的中国克隆Arduino Nano。全新的Linux内核(自3.16起经过测试)具有合适的驱动程序,因此可以轻松将设备检测为USB串行端口。

Arduino意外重启


每次连接终端时,Arduino都会重新启动。原因是终端发送DTR(数据终端就绪)信号,导致设备重启。因此,Arduino IDE将设备置于加载草图的模式。

解决该问题的方法有多种,但事实证明只有一种解决方案有效-必须在RST和GND触点之间安装10µF电解质(下图中的C1)。不幸的是,这也阻止了将草图下载到设备。

结果-该方案如下:


使用KiCad绘制

方案说明
  • R1 — , PC817: (5V — 1.2V / 0.02A) = 190Ω, 180Ω.
  • U2 — Arduino PC. , ( USB ), .
  • JP1 — , . .
  • 1 — , DTR.
  • MB_RST, MB_GND — RESET , RST (GND). , .
  • BTN_RST, BTN_GND — , , , , .


使用WDT时的引导循环(循环重新引导)


ATmega微控制器具有内置的WDT(看门狗定时器)复位机制。但是,所有尝试使用此功能的尝试都会导致启动循环,只有通过关闭电源才能退出该循环。

不久的搜索显示,大多数Arduino克隆的引导程序都不支持WDT。幸运的是,此问题已在Optiboot备用bootloader中解决

为了刷新引导加载程序,您需要一个可以使用SPI协议的程序员,并且Arduino IDE最好亲自了解此设备。在这种情况下,另一个Arduino是理想的。

如果我们以Arduino UNO作为程序员,以及Arduino IDE v1.6.5的最新版本,则算法将如下所示:

  1. boards-1.6.txt optiboot hardware/arduino/avr/boards.txt Arduino IDE.
  2. Arduino Uno, File → Examples → ArduinoISP.
  3. Arduino Nano :
    Arduino Uno ()Arduino Nano (ICSP )
    5V → Vcc
    GND → GND
    D11 → MOSI
    D12 → MISO
    D13 → SCK
    D10 → Reset

    Pin1 (MISO) ← D12Pin2 (Vcc) ← 5V
    Pin3 (SCK) ← D13Pin4 (MOSI) ← D11
    Pin5 (Reset) ← D10Pin6 (GND) ← GND



  4. 在Arduino IDE中的“工具”菜单中,按照屏幕快照中的设置进行设置:
  5. 选择菜单项“ 工具”→“刻录引导程序”,并确保该过程已完成且没有错误。


完成此步骤后,您需要通过选择相同的设置将草图上传到Arduino Nano- :32针cpus上的Optiboot,处理器:ATmega328p,CPU速度:16MHz。

焊接


接下来,您需要焊接所有东西,使其看起来像一件。



在这里,我需要一个USB插头,因为我有一个mini-ITX主板,只有一个用于一对USB2.0的连接器,这在前面板上是必需的,没有任何东西可以连接到USB3.0垫。如有可能,应将此类设备直接连接至主板,以免电线伸出。

通常,焊接不会造成问题,但是在这种情况下,使用面包板,并且有其自身的特点。

如何在面包板上焊接轨道
( , ). .

:



结果:





似乎某些触点的焊接不良,但这只是助焊剂。面包板上的焊料消耗量很大,因此这里可能的所有东西都被焊剂涂抹了。实际上,这是一个很好的例子,说明您无需在焊接后离开产品。必须冲洗掉助焊剂,否则可能会腐蚀化合物。我将添加去洗...更好:



 

软件部分


客观地说,该项目的代码不是特别重要。介绍性内容远非极端,该体系结构用一个短语描述:发送命令-等待答案。为了有秩序,我将在这里描述主要功能,并从我的角度简要地介绍最有趣的观点。

所有代码都在GitHub上发布,因此,如果您熟悉Bash和C / C ++(在Arduino草图的上下文中),那么到此为止的阅读即可结束。如果您有兴趣,可以在这里找到完成的结果

看门狗连接


连接看门狗时,将创建一个包含序列号的设备文件。如果系统中还有其他ttyUSB设备(在我的情况下是调制解调器),则编号存在问题。要唯一标识设备,您需要创建一个具有唯一名称的符号链接。为此,设计了udev,它可能已存在于系统中。

首先,您需要直观地找到连接的看门狗,例如,通过查看系统日志文件。然后,用所需的设备替换/ dev / ttyUSB0,在终端中输入:

udevadm info -a -p "$(udevadm info -q path -n /dev/ttyUSB0)"

输出例子
  looking at device '/devices/pci0000:00/0000:00:14.0/usb1/1-1/1-1.4/1-1.4:1.0/ttyUSB0/tty/ttyUSB0':
    KERNEL=="ttyUSB0"
    SUBSYSTEM=="tty"
    ...
    
  looking at parent device '/devices/pci0000:00/0000:00:14.0/usb1/1-1/1-1.4/1-1.4:1.0/ttyUSB0':
    KERNELS=="ttyUSB0"
    SUBSYSTEMS=="usb-serial"
    DRIVERS=="ch341-uart"
    ...
    
  looking at parent device '/devices/pci0000:00/0000:00:14.0/usb1/1-1/1-1.4/1-1.4:1.0':
    ...
    
  looking at parent device '/devices/pci0000:00/0000:00:14.0/usb1/1-1/1-1.4':
    SUBSYSTEMS=="usb"
    DRIVERS=="usb"
    ATTRS{idVendor}=="1a86"
    ATTRS{idProduct}=="7523"
    ATTRS{product}=="USB2.0-Serial"
    ...


在这种情况下,规则将如下所示:
ACTION=="add", KERNEL=="ttyUSB[0-9]*", SUBSYSTEM=="tty", SUBSYSTEMS=="usb", ATTRS{idVendor}=="1a86", ATTRS{idProduct}=="7523", SYMLINK+="ttyrst-watchdog"


您需要将其放在目录/etc/udev/rules.d中的单独文件中,例如51-ttyrst-watchdog.rules并告诉udev重新加载规则:
udevadm control --reload-rules


从这一刻起,当连接看门狗时,将在所需设备上创建一个链接/ dev / ttyrst-看门狗,稍后将使用它。

Bash脚本(ttyrst-watchdog.sh)


与看门狗的通信以9600波特的速度执行。Arduino可以在终端上高速运行,但处理文本(猫,回声等)的命令仅接收和发送垃圾。这可能只是我的Arduino Nano副本的功能。

对于主计时器重新启动周期和命令行功能,使用一个脚本。原因是两个组件都使用公共资源-设备文件,因此有必要提供对其的同步访问。

同步基本上由一个等待循环组成:
while fuser ${DEVICE} >/dev/null 2>&1; do true; done
并在所需的时间内捕获设备:
cat <${DEVICE}


显然,这样的方案受制于竞赛条件。您可以以成人的方式处理此问题(例如,组织消息队列),但是在这种情况下,正确设置超时时间足以确保您在可接受的时间内得到结果就足够了。实际上,整个脚本都在处理超时。

妖魔化(在后台运行)是使用OpenRC软件包执行的。假定此脚本位于文件/usr/local/bin/ttyrst-watchdog.sh中,而OpenRC脚本位于/etc/init.d/ttyrst-watchdog中

当守护程序停止时,需要正确停用看门狗。为此,脚本设置了需要完成的信号处理程序:
trap deactivate SIGINT SIGTERM
这里出现了一个问题-OpenRC无法停止守护程序,或更确切地说,但并非经常停止。

事实是,kill命令将信号发送到脚本,而用于暂停脚本的睡眠程序在另一个进程中执行,并且未接收到该信号。结果,禁用功能仅在睡眠完成后才启动,这太长了。

解决方案是在后台启动睡眠并等待脚本中的过程完成:
sleep ${SLEEP_TIME} & wait $!  #  $!  ID   


基本常量:

WATCHDOG_ACTIVE-分别为YES或NO,无论是否触发计时器,都发送一个重新启动的信号。
WATCHDOG_TIMER-设置计时器的时间(以秒为单位)。
SLEEP_TIME-必须重新启动计时器的时间(以秒为单位)。它应该比WATCHDOG_TIMER小得多,但不能非常小,以免在系统和设备上产生过多的负载。在当前超时下,合理的最小时间约为5秒。
DEFAULT_LOG_LINES-默认日志命令返回的最近设备日志条目的数量。

脚本命令:

开始-主计时器重启周期的开始。您可以向is_alive函数添加其他验证代码,例如,检查通过ssh连接的可能性。
status-显示设备的状态。
重置 -重置EEPROM(日志数据)并重新启动设备以将看门狗恢复到其原始状态。
log <条目数> -显示指定数量的最近的日志条目。

Arduino草图(ttyrst-watchdog.ino)


要成功编译草图,您需要第三方时间,这对于时间同步是必需的。

草图包含两个文件。这是由于Arduino IDE不接受主文件中声明的结构(struct),因此必须将它们移至外部头文件中。另外,typedef关键字对于声明结构不是必需的,它甚至可能有害。在检查了标准选项后,我找不到合适的语法。其余部分或多或少是标准C ++。

wdt_enable和wdt_reset函数与集成在微控制器中的看门狗一起使用。初始化WDT之后,要记住的主要事情是在所有冗长的操作的主循环和循环内部将其复位。

日志条目被写入非易失性EEPROM存储器,其可用大小可以在logrecord.h中指定,在这种情况下为1024。日志以环的形式制成,分隔符是具有零值的结构。1 KiB EEPROM的最大条目数为203。

仅在时间同步后才记录有关设备加载的记录。同步在重新启动计时器的同时执行,并且在设备初始化期间执行任何命令之前。换句话说,无法将正确的时间与此事件进行比较,并且与工作守护程序隔离的设备重新启动信息不是很有趣。



就这样,谢谢收看!

项目源文件位于GitHub上

Source: https://habr.com/ru/post/zh-CN384467/


All Articles