双节长假一晃就已余额不足了。
其实,我节前立的Flag也早就完成了。而且对“小目标”做了些简化处理:
- 原计划的客户端控制器,以App小游戏或者小车的遥控器的形式去实现。现在的版本是手机去App控制发光二极管开关和闪烁。
- 考虑到我们的重点旨在驱动树莓派的GPIO做信号输出,以小游戏的形式虽然趣味性会高一些,但是似乎有点喧宾夺主。所以大好的时光,我还是决定还是花在刀刃上,在树莓派上所能显现的内容是一样的。
- 至于遥控小车也是挺好玩的,不过手头上的配件不充足,留着下回补充好配件模块回来,再动手去弄吧。
GPIO控制器
上回讲过了GPIO的接线以及如何C#代码驱动它。回到我的Demo,还稍微做了一点封装,以便不用款的Led灯的重用。上一篇说到的 GpioController
的静态实例,是在LedModule构造方法中注进来的。
LedModule
是基于GPIO对Led模块的控制器,主要方法有:
-
SetOn
亮灯 -
SetOff
灭灯 -
Blink
闪烁 -
BlinkWith
与另一个Led交替闪烁
/// <summary>
/// Led 模块的封装
/// </summary>
public class LedModule
{
/// <summary>
/// Pin针脚编号,采用来Board方式来自定
/// </summary>
public int PinIndex { get; private set; }
/// <summary>
/// Pin是否开启
/// </summary>
public bool IsOpenned { get; private set; }
/// <summary>
/// GpioController 实例
/// </summary>
public GpioController GPIO { get; private set; }
/// <summary>
/// LedModule构造方法
/// </summary>
/// <param name="i">PinIndex</param>
/// <param name="ctrl">GpioController</param>
public LedModule(int i, GpioController ctrl)
{
PinIndex = i;
GPIO = ctrl;
}
/// <summary>
/// OpenPin
/// </summary>
public void Open()
{
if (PinIndex < 1 || PinIndex > 40) throw new ArgumentException("index must between 1 - 40.");
try
{
if (!GPIO.IsPinOpen(PinIndex))
{
GPIO.OpenPin(PinIndex, PinMode.Output);
}
IsOpenned = true;
}
catch (Exception ex)
{
IsOpenned = false;
throw ex;
}
}
/// <summary>
/// ClosePin
/// </summary>
public void Close()
{
if (IsOpenned) GPIO.ClosePin(PinIndex);
IsOpenned = false;
}
/// <summary>
/// 闪烁
/// </summary>
/// <param name="interval">闪烁的时间间隔</param>
/// <param name="times">闪烁次数,0-代表无限次数</param>
public void Blink(int interval, int times = 0)
{
if (!IsOpenned) return;
int loop = 0;
ThreadPool.QueueUserWorkItem((x) =>
{
while (IsOpenned)
{
if (loop % 2 == 0) SetOn();
else SetOff();
Thread.Sleep(interval);
loop++;
if (times >= loop) break;
}
});
}
/// <summary>
/// 与另外一个LED灯一起闪烁
/// </summary>
/// <param name="led">另一个灯实例</param>
/// <param name="interval">闪烁的时间间隔</param>
/// <param name="times">闪烁次数,0-代表无限次数</param>
public void BlinkWith(LedModule led, int interval, int times = 0)
{
var led1 = this;
var led2 = led;
int loop = 0;
ThreadPool.QueueUserWorkItem((x) =>
{
while (true)
{
if ((!led1.IsOpenned && !led2.IsOpenned) || times > loop) break;
if (loop % 2 == 0)
{
led1.SetOn();
led2.SetOff();
}
else
{
led2.SetOn();
led1.SetOff();
}
Thread.Sleep(interval);
loop++;
}
});
}
/// <summary>
/// LED亮
/// </summary>
public void SetOn()
{
if (IsOpenned) GPIO.Write(PinIndex, PinValue.High);
}
/// <summary>
/// LED灭
/// </summary>
public void SetOff()
{
if (IsOpenned) GPIO.Write(PinIndex, PinValue.Low);
}
}
手机端APP
手机端应用,我提供用的是一个简单的H5页面。通过调用服务端WebAPI,发出控制LED的命令。
H5App
H5代码
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=0,viewport-fit=cover">
<title>Led控制器</title>
<link href="//smallsea2016.gitee.io/lui/css/lui.css" rel="stylesheet" />
</head>
<body>
<div class="ui_page_wrap">
<header class="ui_page_hd">
<a id="go—back" href="javascript:goBack();" class="ui_back" style="display: none;"></a>
<a>LED控制器</a>
</header>
<div class="ui_page_bd">
<h2 class="ui_list_hd">控制器</h2>
<ul class="ui_list ui_list_arrow">
<li><a href='javascript:resetLed()'>重置LED</a></li>
<li><a href='javascript:setLed("red",2)'>红灯亮</a></li>
<li><a href='javascript:setLed("red",0)'>红灯关闭</a></li>
<li><a href='javascript:setLed("blue",2)'>蓝灯亮</a></li>
<li><a href='javascript:setLed("blue",0)'>蓝灯关闭</a></li>
<li><a href='javascript:blinkLed("red")'>红灯闪烁</a></li>
<li><a href='javascript:blinkLed("blue")'>蓝灯闪烁</a></li>
<li><a href='javascript:blinkLed("all")'>红蓝闪烁</a></li>
</ul>
</div>
</div>
</body>
</html>
<script src="//smallsea2016.gitee.io/lui/js/lui.js"></script>
<script>
function resetLed() {
lui.request({
type: 'POST', url: "/api/led/reset", data: "",
success: function (res) {
console.log(res);
}
});
}
function setLed(color,cmd) {
lui.request({
type: 'POST', url: "/api/led/set", data: { color, cmd },
success: function (res) {
console.log(res);
}
});
}
function blinkLed(color) {
lui.request({
type: 'POST', url: "/api/led/flash", data: { color },
success: function (res) {
console.log(res);
}
});
}
</script>
WebAPI 服务端代码
[Controller(BaseUrl = "/api/led")]
public class Led : BaseController
{
const string LedControl = "LedControl";
[Post]
public JsonResult Reset()
{
Console.WriteLine("Led Reset");
var result = new MsgResult();
Dispatcher.Call(LedControl, new LedEvent("reset", ""));
return Json(result);
}
[Post]
public JsonResult Set(string color, int cmd)
{
Console.WriteLine($"Led Set [{color},{cmd}]");
var result = new MsgResult();
if (!(color == "red" || color == "blue"))
result.SetMessage("led color is not found");
if(result.ret == 0)
{
var evt = new LedEvent("", color);
switch (cmd)
{
case 0:
case 1:
evt.action = "off";
break;
case 2:
evt.action = "on";
break;
}
if(!string.IsNullOrEmpty(evt.action))
result = Dispatcher.Call(LedControl, evt);
}
return Json(result);
}
[Post]
public JsonResult Flash(string color)
{
Console.WriteLine($"Led Flash [{color}]");
var result = new MsgResult();
if (!(color == "red" || color == "blue" || color == "all"))
result.SetMessage("led color is not found");
if (result.ret == 0)
{
result = Dispatcher.Call(LedControl, new LedEvent("blink", color));
}
return Json(result);
}
}
代码粘贴到这里,似乎要稍微解释一下,WebAPI并没有直接操控GPIO,而是通将命令封装成LedEvent
,然后通过Dispatcher.Call
事件派发出去。因此,实际上真正执行GPIO命令的是隐藏在应用内的另外一个服务——LedServerHost
/// <summary>
/// 树莓派GPIO Led灯操控服务
/// </summary>
public class LedServerHost : IHostedService, IDisposable
{
private GpioController controller;
private LedModule ledRed;
private LedModule ledBlue;
public const string LedControl = "LedControl";
public LedServerHost()
{
}
public void Dispose()
{
ledRed.Close();
ledBlue.Close();
controller.Dispose();
Dispatcher.Removesync(LedControl, OnLedCommand);
}
public Task StartAsync(CancellationToken cancellationToken)
{
controller = new GpioController(PinNumberingScheme.Board);
ledRed = new LedModule(11, controller);
ledBlue = new LedModule(16, controller);
Dispatcher.AddAsync(LedControl, OnLedCommand);// 监听WebApi控制事件
return Task.CompletedTask;
}
public Task StopAsync(CancellationToken cancellationToken)
{
this.Dispose();
return Task.CompletedTask;
}
/// <summary>
/// WebApi监听事件的回调方法
/// </summary>
/// <param name="obj"></param>
/// <param name="evt"></param>
/// <returns></returns>
private Task<MsgResult> OnLedCommand(object obj, EventArgs evt)
{
var ledEvt = obj as LedEvent;
var result = new MsgResult();
Console.WriteLine($"action:{ledEvt.action} | target:{ledEvt.target}");
switch (ledEvt.action)
{
case "on":
SetLedOn(ledEvt);
break;
case "off":
SetLedOff(ledEvt);
break;
case "blink":
SetLedBlink(ledEvt);
break;
case "reset":
ledRed.Close();
ledBlue.Close();
break;
}
return Task.FromResult(result);
}
/// <summary>
/// 设置Led闪烁
/// </summary>
/// <param name="ledEvt"></param>
private void SetLedBlink(LedEvent ledEvt)
{
int interval = (ledEvt.interval == 0) ? 500 : ledEvt.interval;
switch (ledEvt.target)
{
case "red":
ledRed.Open();
ledRed.Blink(interval);
break;
case "blue":
ledBlue.Open();
ledBlue.Blink(interval);
break;
case "all":
ledRed.Open();
ledBlue.Open();
ledRed.BlinkWith(ledBlue, interval);
break;
}
}
/// <summary>
/// 设置Led熄灭
/// </summary>
/// <param name="ledEvt"></param>
private void SetLedOff(LedEvent ledEvt)
{
LedModule led = null;
if (ledEvt.target == "red") led = ledRed;
else if (ledEvt.target == "blue") led = ledBlue;
led?.Open();
led?.SetOff();
}
/// <summary>
/// 设置Led点亮
/// </summary>
/// <param name="ledEvt"></param>
private void SetLedOn(LedEvent ledEvt)
{
LedModule led = null;
if (ledEvt.target == "red") led = ledRed;
else if (ledEvt.target == "blue") led = ledBlue;
led?.Open();
led?.SetOn();
}
}
至此,我们熟悉的GpioController
终于又显现出来了。我的代码也宣告粘贴完毕。国庆几天假期,我对着树莓派也就折腾这了个这样的东西出来……
此刻,我的心情有点小兴奋,也有点小失望。兴奋的自然是自己向IoT领域的探索,终于迈出成功的小半步;失望的是个人能力所限,出来的作品确实也不够高端不够精彩。树莓派的系列就此暂告一段落,相信不久我又会回来的:)
网友评论