远程启动和电源控制 PC 从属设备






4.93/5 (58投票s)
从另一台 PC 上的软件控制远程开启和关闭一台 PC。
摘要
从另一台 PC 上的软件控制远程开启和关闭一台 PC。这在驱动程序和其他开发测试中很有用。
引言
我正在深入研究 C# 开源操作系统 (Cosmos)。虽然 VMware 很棒,但在真实硬件上进行测试效果更好。为此,我想让部署自动化,但这需要一些定制工作。启动可以通过 PXE 完成,调试可以通过 null modem 串行电缆完成。但谁愿意为每次构建手动开启和关闭 PC 呢?
为了解决这个问题,我需要找到一种方法,让我的主 PC 上的软件能够开启和关闭从机 PC。最初考虑了几种替代方案。
WOL (网络唤醒)
可以通过网络发送一个包含要开启的 PC MAC 地址的“魔法数据包”。然而,WOL 没有关机或重置的功能。
USB 电源控制器
亚马逊上有许多基于 USB 和以太网的电源开关,可以开启和关闭电源插座。PC 可以被编程为在断电后恢复。然而,这些设备往往非常昂贵,而低端产品在质量方面的评论很糟糕。这些也无法为我所需提供完美的控制。
定制解决方案
我发现一篇博客讨论了一位最终用户尝试使用串行端口的线路来完成我正在做的事情的几次尝试。这实际上比想象中要复杂,因为需要使用至少光耦进行隔离。所需的原理图需要一些非常特殊的、难以获得的零件。
继电器控制器
我发现了几种现成的继电器控制器可供购买。我评估了许多,最终选择了 CanaKit UK1104。价格为 59.95 美元外加运费。它支持 4 个继电器,并带有输入传感器,我可以用它们来检测 PC 状态。这实际上很重要,因为切换电源按钮既用于开启 PC,也用于关闭 PC,所以我需要知道我将要开启还是关闭。
要使用继电器,还需要一个 +9V 或 +12V 的电源。我已将许多 12V 应用连接到计算机的 PSU,但由于该设备实际上用于开启 PC,并且 ATX 计算机 PSU 没有始终在线的 +12V 线,因此需要额外的电源。
选择主板
PC 的其他一切都是标准的,而且很多是我从计算机维修设施获得的二手零件。在我的情况下,主板需要一些特定的项目
- 板载以太网,支持 PXE 启动。大多数主板都有此功能。
- 串行端口,或串行接口。
- 价格便宜。
- 板载显卡
经过一些评估,我选择了 Intel D425KT。它是一款 Mini-ITX 主板,带有板载串行端口,并通过接口提供第二个串行端口。它带有 Atom CPU,批发价约 50 美元,零售价 60-75 美元。
组装 PC
首先,我组装了 PC 并确保它正常工作。下一步是添加继电器板并对线路进行一些自定义更改。组装好的 PC 看起来是这样的。
现在我们来看看主板的前置面板接口。每块主板的引脚位置都不同,因此查阅手册了解正确的连接非常重要。错误的连接可能会导致主板短路。
我们感兴趣的连接是绿色和红色的。为了方便连接,找到一个旧机箱并取出电源指示灯和电源开关。它们将带有连接器,可用于确保不会发生短路。有时它们不会完全匹配,但可以分开,用小刀提取引线并重新排列。
(注意,此照片中的不是 D425KT)
首先,我们需要将电源开关连接到 6 号和 8 号引脚。剪断电源线并将其连接到继电器上的 COM 和 NO。我使用的是继电器 4。COM 是接地,NO 是继电器开启时与 COM 短接。NC 是继电器关闭时与 COM 短接,但我们的应用不需要它。由于它是一个开关,没有极性,因此连接引脚 6 和 8 的顺序无关紧要,只要其中一个连接到 COM,另一个连接到 NO 即可。继电器是螺丝端子,无需焊接,只需一把小螺丝刀。我还将原有的物理开关进行了剪接,并直接连接到继电器上形成剪接,这样我仍然可以手动操作 PC。
接下来,我们需要连接电源 LED。这是一个 +5V 线,在 PC 开启时供电。它用于连接电源指示灯,但由于它是 +5V,我们可以用它来检测 PC 是否开启。为此,剪断电源线并将 +5V 线连接到控制器卡上的通道 1。只有 +5V 线被使用,接地线仅保留连接到主板。在线路上,彩色线用于正极,塑料连接器通常也有标记。
如果一根不是彩色的,另一根是白色的,它们可能分别是红色和黑色的。红色始终是正极,黑色始终是地线。这些电线中不太常见的是条纹系统,因为它们更小。
如果都是白色的,其中一根会有条纹。有条纹的那根是正极。
我也将指示灯重新连接了,这样我也有一个可见的指示器。为了连接有传感器引脚的电路板,我剪掉了硬盘指示灯的塑料连接器并使用了它。并非所有通道都是均等的,有些使用不同的电压水平。对我们来说,任何一种都可以(TTL 或 Schottky),但 Schottky 对我们来说更好,所以我选择了通道 1。
所以,完成后应该看起来像这样。
或者真实世界的版本。
最后,由于 PC 将通过网络启动并使用串行端口进行调试,因此背面也有重要的连接。
软件
现在我们可以开始了!继电器控制器安装为 USB 转串行设备,这意味着它可以被终端程序寻址,或者通过使用串行端口用任何编程语言非常轻松地进行寻址。
继电器控制器操作手册中列出的文本命令。为了测试它,我们现在可以打开一个终端程序并发出一些命令。这是 TeraTerm,一个免费的终端程序。
首先,我们检查通道 1 的状态。它是 0,这意味着计算机已关闭。
然后我们必须切换开关,因为电源开关是按下后释放的。也就是说,我们通过开启继电器然后关闭它来模拟按下并释放。在软件中,请确保在开启和关闭之间延迟约 500 毫秒,以便主板识别按下。记住,我们正在模拟一个人的手指。
最后,我们再次检查通道 1,可以看到计算机现在已开启。
其他继电器之一可以连接到重置引脚,但不需要。要重置 PC,只需将其关闭,然后再开启。关闭 PC 的方法与开启 PC 的方法相同,只需“按下”按钮即可。
视频
我还发布了一个 系统运行的视频。
来源
此视频中使用的源代码可以轻松从 Cosmos 项目源中提取。这是在 Cosmos (C# 开源操作系统) 中使用的相关代码。
<pre wrap="true">using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Text;
using System.Threading;
using System.IO.Ports;
using Cosmos.Build.Common;
namespace Cosmos.Debug.VSDebugEngine.Host {
public class Slave : Base {
string mPortName;
SerialPort mPort;
Thread mPowerStateThread;
public Slave(NameValueCollection aParams, bool aUseGDB)
: base(aParams, aUseGDB) {
var xPort = mParams[BuildProperties.SlavePortString];
if (xPort == "None") {
throw new Exception("No slave port is set.");
}
var xParts = xPort.Split(' ');
mPortName = xParts[1];
}
string WaitForPrompt() {
var xSB = new StringBuilder();
char xLastChar = ' ';
char xChar = ' ';
while (true) {
xLastChar = xChar;
xChar = (char)mPort.ReadChar();
xSB.Append(xChar);
if (xChar == ':' && xLastChar == ':') {
break;
}
}
// Remove ::
xSB.Length = xSB.Length - 2;
return xSB.ToString();
}
void TogglePowerSwitch() {
Send("REL4.ON");
Thread.Sleep(500);
Send("REL4.OFF");
}
bool IsOn() {
var xResult = Send("CH1.GET").Split('\n');
return xResult[1][0] == '1';
}
string Send(string aData) {
// Dont use writeline, it only sends /n or /r (didnt bother to find out which, we need both)
mPort.Write(aData + "\r\n");
return WaitForPrompt();
}
void WaitPowerState(bool aOn) {
int xCount = 0;
while (IsOn() == !aOn) {
Thread.Sleep(250);
xCount++;
// 5 seconds
if (xCount == 20) {
throw new Exception("Slave did not respond to power command.");
}
}
}
public override void Start() {
mPort = new SerialPort(mPortName);
mPort.Open();
Send("");
// Set to digital input
Send("CH1.SETMODE(2)");
if (IsOn()) {
TogglePowerSwitch();
WaitPowerState(false);
// Small pause for discharge
Thread.Sleep(1000);
}
TogglePowerSwitch();
// Give PC some time to turn on, else we will detect it as off right away.
WaitPowerState(true);
if (OnShutDown != null) {
mPowerStateThread = new Thread(delegate() {
while (true) {
Thread.Sleep(1000);
if (!IsOn()) {
mPort.Close();
OnShutDown(this, EventArgs.Empty);
break;
}
}
});
mPowerStateThread.Start();
}
}
public override void Stop() {
if (mPowerStateThread != null) {
mPowerStateThread.Abort();
mPowerStateThread.Join();
}
if (IsOn()) {
TogglePowerSwitch();
WaitPowerState(false);
}
mPort.Close();
}
}
}