美文网首页
再玩树莓派(四)小实验Demo

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

作者: ChenReal | 来源:发表于2020-10-11 23:41 被阅读0次

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

相关文章

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

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

  • 树莓派技术网站导航

    树莓派官网 安装 树莓派Mac OS X下安装系统开启ssh登陆 使用教程 树莓派实验室树莓派吧树莓派中文站 项目...

  • 树莓派上手资料

    树莓派开箱上手教程树莓派下载资料使用手机连接树莓派1使用手机连接树莓派2树莓派实验室无显示屏启动树莓派 如何用pu...

  • 学习网址链接

    树莓派官网 可以下载raspbian系统和第三方系统树莓派论坛树莓派实验室,里面有很多项目和教程树莓派3教程,这是...

  • Raspbian 中国软件源

    标签: 树莓派 本文来自:树莓派实验室链接地址:http://shumeipai.nxez.com/2013/08...

  • 再玩树莓派(三)GPIO

    前两篇,我们已经把操作系统和软件的运行环境都搭建好了,到这里终于可以开始把正菜端上桌了。那么,下面我们开始探索一下...

  • Linux的免密码登陆

    最近在玩树莓派的时候,mac连接树莓派老是要输入密码。于是网上找了Linux可以免密码登陆的操作。(树莓派它的操作...

  • 基于ssh的稳定内网穿透and远程控制

    Scenario 上图的树莓派里面跑着我用于Blockchain + IOT的一个小demo,但是就面临着内网穿透...

  • 树莓派Raspbian更新源(Debian)| 完整解决步骤

    转载自:树莓派更新源更换 | 树莓派小无相系列 型号: 树莓派 3b+系统:Raspbian系统(Debian)本...

  • 树莓派综合项目2:智能小车(二)tkinter图形界面控制

    一、介绍   阅读本篇文章前建议先参考前期文章:  树莓派基础实验34:L298N模块驱动直流电机实验  树莓派综...

网友评论

      本文标题:再玩树莓派(四)小实验Demo

      本文链接:https://www.haomeiwen.com/subject/xvmepktx.html