再玩树莓派(四)小实验Demo

2020-10-11  本文已影响0人  ChenReal

双节长假一晃就已余额不足了。
其实,我节前立的Flag也早就完成了。而且对“小目标”做了些简化处理:

GPIO控制器

上回讲过了GPIO的接线以及如何C#代码驱动它。回到我的Demo,还稍微做了一点封装,以便不用款的Led灯的重用。上一篇说到的 GpioController的静态实例,是在LedModule构造方法中注进来的。
LedModule 是基于GPIO对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领域的探索,终于迈出成功的小半步;失望的是个人能力所限,出来的作品确实也不够高端不够精彩。树莓派的系列就此暂告一段落,相信不久我又会回来的:)

上一篇 下一篇

猜你喜欢

热点阅读