zoukankan      html  css  js  c++  java
  • 使用asp.net core 3.0 搭建智能小车2

      上一篇中我们把基本的运行环境搭建完成了,这一篇中,我们实战通过树莓派B+连接HC-SR04超声波测距传感器,用c# GPIO控制传感器完成距离测定,并将距离显示在网页上.

    1.HC-SR04接线

      传感器如下图:

      

      HC-SR04 模块可以测量 3cm – 4m 的距离,精确度可以达到 3mm.这个模块包括 超声波发射器、超声波接收器和控制电路三部分.该传感器有4个引脚:

      VCC,   超声波模块电源脚,接5V电源即可

      Trig,  超声波发送脚

      Echo,超声波接收检测脚

      GND,接地

    1.1HC-SR04超声波模块工作原理:      

      (1) 树莓派向 Trig 脚发送一个至少 10us 的脉冲信号。
      (2) HC-SR04 接收到信号,开始发送超声波,并把 Echo置为高电平,然后准备接收返回的超声波
      (3) HC-SR04 接收到返回的超声波,把 Echo 置为低电平。
      (4) Echo 高电平持续的时间就是超声波从发射到返回的时间间隔。
      (5) 计算距离:距离(单位:m)  =  (start - end) * 声波速度 / 2 

            

    1.2 接线

          4 个引脚由 2 个电源引脚(Vcc 、GND)和 2 个控制引脚(Trig、Echo)组成。
      Vcc 和 Gnd 接 5v DC 电源,但不推荐用独立电源给它供电,应使用树莓派的 GPIO 口输出 5v 和 Gnd 给它供电。不然会影响这个模块的运行。
      Trig 引脚用来接收来自树莓派的控制信号。接任意 GPIO 口。
      Echo 引脚用来发送测距结果给树莓派。接任意 GPIO 口。

      对应树莓派40pin引脚对照表:

         我这里把Trlg接到23,Echo接到24,VCC接到5V,(BCM编码方式)GND接GND:

        

        这样线就接好了.开始编码阶段.

    2.c# 程序

      打开上一章建立的空项目首先在Models新建一个类 SiteConfig:

     public class SiteConfig
     {
           
            /// <summary>
            /// 超声波控制端 默认23
            /// </summary>
            public int TriggerPin { get; set; }
    
            /// <summary>
            /// 超声波接收端 默认24
            /// </summary>
            public int EchoPin { get; set; }
         
     }

    之后在 appsettings.json 中添加 这个节点:

    "SiteConfig": { 
        "TriggerPin": 23,
        "EchoPin": 24,
      }

    在 Startup.cs 类的ConfigureServices 方法添加

     services.Configure<SiteConfig>(Configuration.GetSection("SiteConfig"));

    这样通过依赖注入我们就可以使用我们配置的变量了.这些无关紧要的东西写完后,我们在项目中新建文件夹 Playground 并在这个文件下建立Ultrasonic文件夹:

     

    在Ultrasonic里面建立三个文件 IHcsr04Client.cs,Hcsr04Client.cs,Hcsr04ReadEventArgs.cs

    IHcsr04Client:

     public interface IHcsr04Client
        {
            event EventHandler<Hcsr04ReadEventArgs> OnDataAvailable;
            void Start();
            void Stop();
        }

    Hcsr04Client:

     public class Hcsr04Client : IHcsr04Client
        {
            private readonly int _echo;
            private readonly int _trigger;
            private int _lastMeasurment = 0;       
            public const int NoObstacleDistance = -1;
            private readonly object _locker = new object();
            private readonly GpioController _controller;
            private readonly Stopwatch _timer = new Stopwatch();
            public event EventHandler<Hcsr04ReadEventArgs> OnDataAvailable;
    
            public bool IsRunning { get; set; }
    
            public Hcsr04Client(IOptions<SiteConfig> option, GpioController controller)
            {
                _echo = option.Value.EchoPin;
                _trigger = option.Value.TriggerPin;
                _controller = controller;
            }
    
            public void Start()
            {
                lock (_locker)
                {
                    IsRunning = true;
                    if (!_controller.IsPinOpen(_echo))
                        _controller.OpenPin(_echo, PinMode.Input);
    
                    if (!_controller.IsPinOpen(_trigger))
                    {
                        _controller.OpenPin(_trigger, PinMode.Output);
                        _controller.Write(_trigger, PinValue.Low);
                    }
                    Task.Run(() => PerformContinuousReads());
                }
            }
            public void Stop()
            {
                lock (_locker)
                {
                    IsRunning = false;
                    if (_controller.IsPinOpen(_trigger))
                        _controller.ClosePin(_trigger);
                    if (_controller.IsPinOpen(_echo))
                        _controller.ClosePin(_echo);
                }
            }
            private void PerformContinuousReads()
            {
                while (IsRunning)
                {
                    var sensorData = RetrieveSensorData();
    
                    if (!IsRunning) continue;
                    OnDataAvailable?.Invoke(this, sensorData);
                    Thread.Sleep(200);
                }
            }
            private Hcsr04ReadEventArgs RetrieveSensorData()
            {
                try
                {
                    _timer.Reset();
    
                    while (Environment.TickCount - _lastMeasurment < 60)
                    {
                        Thread.Sleep(TimeSpan.FromMilliseconds(Environment.TickCount - _lastMeasurment));
                    }
    
                    _controller.Write(_trigger, PinValue.High);     // trigger上高电平
                    Thread.Sleep(TimeSpan.FromMilliseconds(0.01));  // 持续一段时间,发出足够的脉冲
                    _controller.Write(_trigger, PinValue.Low);      // 设置低电平
    
    
                    if (!GpioEX.WaitForValue(_controller, _echo, PinValue.Low)) // echo等待低电平结束,记录时间
                        throw new TimeoutException();
    
                    _lastMeasurment = Environment.TickCount;
    
                    _timer.Start();
    
                    if (!GpioEX.WaitForValue(_controller, _echo, PinValue.High)) // echo等待高电平结束,记录时间
                        throw new TimeoutException();
    
                    _timer.Stop();
    
                    TimeSpan elapsed = _timer.Elapsed;
    
    
                    var distance = elapsed.TotalMilliseconds / 2.0 * 34.3; 
    
                    return new Hcsr04ReadEventArgs(distance);
    
                }
                catch
                {
                    return Hcsr04ReadEventArgs.CreateInvalidReading();
                }
    
            }
        }

    Hcsr04ReadEventArgs:

     public class Hcsr04ReadEventArgs : EventArgs
        {
            internal Hcsr04ReadEventArgs(double distance)
            {
                Distance = distance;
            }
    
            private Hcsr04ReadEventArgs(bool isValid) : this(Hcsr04Client.NoObstacleDistance)
            {
                IsValid = isValid;
            }
    
            /// <summary>
            /// 读数是否有效.
            /// </summary>
            public bool IsValid { get; } = true;
    
            /// <summary>
            /// 是否检测到任何障碍物.
            /// </summary>
            public bool HasObstacles => Distance != Hcsr04Client.NoObstacleDistance;
    
            /// <summary>
            /// 获取到障碍物的实际距离,以厘米为单位.
            /// </summary>
            public double Distance { get; }
    
    
            internal static Hcsr04ReadEventArgs CreateInvalidReading() => new Hcsr04ReadEventArgs(false);

    最后在注入到服务:

     services.AddSingleton<IHcsr04Client, Hcsr04Client>();

    代码都非常简单,也有相关注释.大家看一眼就懂了.之后就是网网页上展示了.

     先弄一个Controller,在Controllers文件新建一个Hcsr04Controller和CarController的控制器:

     public class Hcsr04Controller : Controller
        {
            public static string iscsb = "stop";
            private readonly IHcsr04Client _hcsr04;
            private readonly IHubContext<ChatHub> _chatHub;
            public Hcsr04Controller(IHcsr04Client hcsr04, IHubContext<ChatHub> chatHub)
            {
                _hcsr04 = hcsr04;
                _chatHub = chatHub;
            }
            public async Task<IActionResult> Hcsr04On()
            {
                _hcsr04.OnDataAvailable += async (s, e) =>
                {
                    if (!e.IsValid)
                    {
                        await _chatHub.Clients.All.SendAsync("ReceiveMessage", "1", "声波没有返回,被折射掉了.");
                    }
                    else if (e.HasObstacles)
                    {
                        await _chatHub.Clients.All.SendAsync("ReceiveMessage", "1", $"距离:{e.Distance:N2}cm.");
                    }
                    else
                    {
                        await _chatHub.Clients.All.SendAsync("ReceiveMessage", "1", "未检测到障碍物.");
                    }
                };
                _hcsr04.Start();
                iscsb = "start";
                await _chatHub.Clients.All.SendAsync("ReceiveMessage", "50", "开启超声波通知.");
                return Content("超声波打开");
            }
            public async Task<IActionResult> Hcsr04Off()
            {
                _hcsr04.Stop();
                iscsb = "stop";
                await _chatHub.Clients.All.SendAsync("ReceiveMessage", "51", "超声波关闭通知.");
                return Content("超声波关闭");
            }
        }

    我这里面使用了signalR,方便数据到达时候在web上面展示,SignalR的内容这里就不说了,就是简单的使用.

    CarController就什么都不用改了.之后新建一个视图:

    代码:

    @{
        ViewData["Title"] = "智能小车控制面板";
    }
    @section Css{
        <link href="~/css/car.css" rel="stylesheet" />
    }
    <div class="text-center">
        <h5 class="display-4">控制面板</h5>
        <p>温度:<span id="wd">35°C</span> 湿度:<span id="sd">50%</span></p>
    </div>
    <ul class="Switch">
        <li>
            <input type="checkbox" name="Storage" id="csb" onclick="KZCSB(this)" />
            超声波
            <label for="csb"><em></em></label>
        </li>
        <li>
            <input type="checkbox" name="Storage2" id="bz" onclick="KZBZ(this)" />
            红外避障
            <label for="bz"><em></em></label>
        </li>
        <li>
            <input type="checkbox" name="Storage2" id="wf" onclick="KZWIFIYK(this)" checked="checked" />
            WiFi遥控
            <label for="wf"><em></em></label>
        </li>
        <li>
            <input type="checkbox" name="Storage2" id="hw" onclick="KZHWYK(this)" />
            红外遥控
            <label for="hw"><em></em></label>
        </li>
    </ul>
    
    <div class="control-wrapper">
        <div class="control-btn control-top" id="up" onclick="carmove(this,'up')">
            <i class="fa fa-chevron-up">∧</i>
            <div class="control-inner-btn control-inner"></div>
        </div>
        <div class="control-btn control-left" id="left" onclick="carmove(this,'left')">
            <i class="fa fa-chevron-left"><</i>
            <div class="control-inner-btn control-inner"></div>
        </div>
        <div class="control-btn control-bottom" id="down" onclick="carmove(this,'down')">
            <i class="fa fa-chevron-down">∨</i>
            <div class="control-inner-btn control-inner"></div>
        </div>
        <div class="control-btn control-right" id="right" onclick="carmove(this,'right')">
            <i class="fa fa-chevron-right">></i>
            <div class="control-inner-btn control-inner"></div>
        </div>
        <div class="control-round" id="pause" onclick="carmove(this,'pause')">
            <div class="control-round-inner">
                <i class="fa fa-pause-circle">P</i>
            </div>
        </div>
    </div>
    
    <div class="c-box">
        <div class="c-left">
            <h5 style="text-align:center;margin-top:10px;">超声波数据</h5>
            <p id="csbdata" style="color:Red;text-align:center"></p>
        </div>
        <div class="c-right">
            <h5 style="text-align:center;margin-top:10px;">红外避障数据</h5>
            <p id="bzdata" style="color:Red;text-align:center"></p>
        </div>
    </div>
    
    <div>
        <label class="demo--label">
            <input class="demo--radio" type="radio" name="demo-radio" checked="checked" value="0.4">
            <span class="demo--radioInput"></span>40
        </label>
        <label class="demo--label">
            <input class="demo--radio" type="radio" name="demo-radio" value="0.6">
            <span class="demo--radioInput"></span>60
        </label>
        <label class="demo--label">
            <input class="demo--radio" type="radio" name="demo-radio" value="0.8">
            <span class="demo--radioInput"></span>80
        </label>
        <label class="demo--label">
            <input class="demo--radio" type="radio" name="demo-radio" value="1.0">
            <span class="demo--radioInput"></span>100
        </label>
    </div>
    
    @section Scripts{
        <script src="~/js/signalr.js"></script>
        <script>
            $(document).ready(function () {
                if (iscsb=="start") {
                   $("#csb").prop("checked",true);
                } else {
                   $("#csb").prop("checked", false);
                }
               
            });
    
            // 控制超声波
            function KZCSB(th) {
                if ($(th).is(':checked')) {
                    $.get("/Hcsr04/Hcsr04On", function (data) {
                        iscsb = 'start';
                        console.log(data);
                    });
                } else {
                    $.get("/Hcsr04/Hcsr04Off", function (data) {
                        $("#csbdata").empty();
                        iscsb = "stop";
                        console.log(data);
                    });
                }
            }
    // signalR 接受传感器数据
            var connection = new signalR.HubConnectionBuilder().withUrl("/chatHub").build();
    
            connection.on("ReceiveMessage", function (type, msg) {
                switch (type) {
                    case "1":
                        $("#csbdata").text(msg);
                        break;             case "50":
                        $("#csb").prop("checked", true);break;
                    case "51":
                        $("#csb").prop("checked", false);
                        $("#csbdata").empty();break;              
                }
            });
    
            connection.start().then(function () { }).catch(function (err) {
                return console.error(err.toString());
            });
    
            connection.onclose(async () => {
                $("#csbdata").empty();
                $("#bzdata").empty();
                console.info('监听到链接关闭');
                await start();
            });
    
            async function start() {
                try {
                    await connection.start();
                    console.log("connected");
                } catch (err) {
                    console.log(err);
                    setTimeout(() => start(), 5000); // 断线重连
                }
            };
        </script>
    }

    编码阶段就完成了,没啥含量,简单粗暴,能用就行.现在我们把代码生成完成把生成的一堆文件都ftp到我们树莓派的目录上面.

    在树莓派上我们要重启我们的站点才能生效:

    sudo systemctl restart kestrel-carapp.service
    

    之后在浏览器输入树莓派的IP来看效果吧:

      今天超声波模块就弄完了.下一章把红外避障和电机驱动上,让它跑起来

  • 相关阅读:
    es6 学习笔记
    正则表达式 学习笔记
    knockout 学习笔记
    错误笔记
    zepto 学习笔记
    Dialog提示框
    setContentView( )方法
    SVG图形之几何图形——学习笔记2
    SVG图形之SVG文件如何嵌入HTML文档——学习笔记1
    getchar()的用法
  • 原文地址:https://www.cnblogs.com/Gorillaz/p/11818145.html
Copyright © 2011-2022 走看看