上一篇中我们把基本的运行环境搭建完成了,这一篇中,我们实战通过树莓派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": ,
"EchoPin": ,
}

在 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 = ;
public const int NoObstacleDistance = -;
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();
}
}
private Hcsr04ReadEventArgs RetrieveSensorData()
{
try
{
_timer.Reset(); while (Environment.TickCount - _lastMeasurment < )
{
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", "", "声波没有返回,被折射掉了.");
}
else if (e.HasObstacles)
{
await _chatHub.Clients.All.SendAsync("ReceiveMessage", "", $"距离:{e.Distance:N2}cm.");
}
else
{
await _chatHub.Clients.All.SendAsync("ReceiveMessage", "", "未检测到障碍物.");
}
};
_hcsr04.Start();
iscsb = "start";
await _chatHub.Clients.All.SendAsync("ReceiveMessage", "", "开启超声波通知.");
return Content("超声波打开");
}
public async Task<IActionResult> Hcsr04Off()
{
_hcsr04.Stop();
iscsb = "stop";
await _chatHub.Clients.All.SendAsync("ReceiveMessage", "", "超声波关闭通知.");
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">°C</span> 湿度:<span id="sd">%</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>
</label>
<label class="demo--label">
<input class="demo--radio" type="radio" name="demo-radio" value="0.6">
<span class="demo--radioInput"></span>
</label>
<label class="demo--label">
<input class="demo--radio" type="radio" name="demo-radio" value="0.8">
<span class="demo--radioInput"></span>
</label>
<label class="demo--label">
<input class="demo--radio" type="radio" name="demo-radio" value="1.0">
<span class="demo--radioInput"></span>
</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 "":
$("#csbdata").text(msg);
break; case "":
$("#csb").prop("checked", true);break;
case "":
$("#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(), ); // 断线重连
}
};
</script>
}

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

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

sudo systemctl restart kestrel-carapp.service

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

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

使用asp.net core 3.0 搭建智能小车2的更多相关文章

  1. 使用asp.net core 3.0 搭建智能小车1

    跟随.net core 3.0 一起发布的System.Device.Gpio 1.0已经可以让我们用熟悉的C#原汁原味的开发莓派上面的GPIO了.并且在 Iot.Device.Bindings这个包 ...

  2. asp.net core 2.0+sqlsugar搭建个人网站系列(0)

    一些废话 马上就要过年了,回顾这一年最大的收获就是技术有了很大的提升,其他的方面没有什么改变,现在还是单身小屌丝一枚. 这一年来学习的主要重点就是asp.net core,中间也使用 core+EF做 ...

  3. Asp.Net Core 2.0 项目实战(2)NCMVC一个基于Net Core2.0搭建的角色权限管理开发框架

    Asp.Net Core 2.0 项目实战(1) NCMVC开源下载了 Asp.Net Core 2.0 项目实战(2)NCMVC一个基于Net Core2.0搭建的角色权限管理开发框架 Asp.Ne ...

  4. win10下ASP.NET Core 2.0部署环境搭建(转)

    此文用于记录在win10环境下,新建的Asp.net Core 2.0 Web应用项目如何运行在IIS上 一.运行环境 操作系统: Window10 家庭中文版 版本 10.0.15063 版本 15 ...

  5. 初识ASP.NET Core 1.0

    本文将对微软下一代ASP.NET框架做个概括性介绍,方便大家进一步熟悉该框架. 在介绍ASP.NET Core 1.0之前有必要澄清一些产品名称及版本号.ASP.NET Core1.0是微软下一代AS ...

  6. ASP.NET Core 1.0 静态文件、路由、自定义中间件、身份验证简介

    概述 ASP.NET Core 1.0是ASP.NET的一个重要的重新设计. 例如,在ASP.NET Core中,使用Middleware编写请求管道. ASP.NET Core中间件对HttpCon ...

  7. 在 Mac OS 上使用 TypeScript 编写 ASP.NET Core 1.0 应用

    var appInsights=window.appInsights||function(config){ function r(config){t[config]=function(){var i= ...

  8. 【原生态跨平台:ASP.NET Core 1.0(非Mono)在 Ubuntu 14.04 服务器上一对一的配置实现-篇幅1】

    鸡冻人心的2016,微软高产年. build 2016后 各种干货层出不穷. 1 Win10 集成了bash  ,实现了纳德拉的成诺,Microsoft Love Linux!!! 2 跨平台  ,收 ...

  9. ASP.NET Core 使用 JWT 搭建分布式无状态身份验证系统

    为什么使用 Jwt 最近,移动开发的劲头越来越足,学校搞的各种比赛都需要用手机 APP 来撑场面,所以,作为写后端的,很有必要改进一下以往的基于 Session 的身份认证方式了,理由如下: 移动端经 ...

随机推荐

  1. 跨域及JSONP原理

    什么是跨域:a.com 域名下的js无法操作b.com或是c.a.com域名下的对象 为什么浏览器要引入跨域问题? 跨域问题来源于浏览器的同源策略,为啥要有这个策略呢? 为了安全.假设现在有a.com ...

  2. LeetCode正则表达式匹配

    题目描述 给你一个字符串 s 和一个字符规律 p,请你来实现一个支持 '.' 和 '*' 的正则表达式匹配. '.' 匹配任意单个字符 '*' 匹配零个或多个前面的那一个元素 所谓匹配,是要涵盖 整个 ...

  3. Scala 学习笔记之集合(5)

    import collection.mutable.Buffer object CollectionDemo6 { def main(args: Array[String]): Unit = { // ...

  4. java的日期时间处理(待更新)

    1. /*      * 将时间转换为时间戳      */        public static String dateToStamp(String s) throws ParseExcepti ...

  5. yii2 rules 规则

    required : 必须值验证属性 [['字段名'],required,'requiredValue'=>'必填值','message'=>'提示信息']; #说明:CRequiredV ...

  6. windows下将jar文件设置为系统服务

    jar文件的执行需要java环境,怎么配置环境相信不用说了 因为不想每次开机都手动启动一次程序,那么我们就需要把它配置成开机自启动的服务,下面就来讲一种方法 首先,我们知道jar文件的执行命令为 ja ...

  7. 数据的异构实战(一) 基于canal进行日志的订阅和转换

    什么是数据的异构处理.简单说就是为了满足我们业务的扩展性,将数据从某种特定的格式转换到新的数据格式中来. 为什么会有这种需求出现呢? 传统的企业中,主要都是将数据存储在了关系型数据库中,例如说MySQ ...

  8. C语言--最大公约数

    //辗转相除法 int main() { int a,b; int t; scanf("%d %d", &a,&b); ) { t = a%b; a = b; b ...

  9. 2019滴滴java面试总结 (包含面试题解析)

    2019滴滴java面试总结  (包含面试题) 本人6年开发经验.今年年初找工作,在互联网寒冬下成功拿到阿里巴巴.今日头条.滴滴等公司offer,岗位是既有php也有Java后端开发,最终选择去了滴滴 ...

  10. Redis 的底层数据结构(整数集合)

    当一个集合中只包含整数,并且元素的个数不是很多的话,redis 会用整数集合作为底层存储,它的一个优点就是可以节省很多内存,虽然字典结构的效率很高,但是它的实现结构相对复杂并且会分配较多的内存空间. ...