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

时间:2020-10-12 02:09   作者:ChenReal    阅读:183

双节长假一晃就已余额不足了。 其实,我节前立的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领域的探索,终于迈出成功的小半步;失望的是个人能力所限,出来的作品确实也不够高端不够精彩。树莓派的系列就此暂告一段落,相信不久我又会回来的:)

 

评论
0/200