使用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": ,
"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的更多相关文章
- 使用asp.net core 3.0 搭建智能小车1
跟随.net core 3.0 一起发布的System.Device.Gpio 1.0已经可以让我们用熟悉的C#原汁原味的开发莓派上面的GPIO了.并且在 Iot.Device.Bindings这个包 ...
- asp.net core 2.0+sqlsugar搭建个人网站系列(0)
一些废话 马上就要过年了,回顾这一年最大的收获就是技术有了很大的提升,其他的方面没有什么改变,现在还是单身小屌丝一枚. 这一年来学习的主要重点就是asp.net core,中间也使用 core+EF做 ...
- 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 ...
- win10下ASP.NET Core 2.0部署环境搭建(转)
此文用于记录在win10环境下,新建的Asp.net Core 2.0 Web应用项目如何运行在IIS上 一.运行环境 操作系统: Window10 家庭中文版 版本 10.0.15063 版本 15 ...
- 初识ASP.NET Core 1.0
本文将对微软下一代ASP.NET框架做个概括性介绍,方便大家进一步熟悉该框架. 在介绍ASP.NET Core 1.0之前有必要澄清一些产品名称及版本号.ASP.NET Core1.0是微软下一代AS ...
- ASP.NET Core 1.0 静态文件、路由、自定义中间件、身份验证简介
概述 ASP.NET Core 1.0是ASP.NET的一个重要的重新设计. 例如,在ASP.NET Core中,使用Middleware编写请求管道. ASP.NET Core中间件对HttpCon ...
- 在 Mac OS 上使用 TypeScript 编写 ASP.NET Core 1.0 应用
var appInsights=window.appInsights||function(config){ function r(config){t[config]=function(){var i= ...
- 【原生态跨平台:ASP.NET Core 1.0(非Mono)在 Ubuntu 14.04 服务器上一对一的配置实现-篇幅1】
鸡冻人心的2016,微软高产年. build 2016后 各种干货层出不穷. 1 Win10 集成了bash ,实现了纳德拉的成诺,Microsoft Love Linux!!! 2 跨平台 ,收 ...
- ASP.NET Core 使用 JWT 搭建分布式无状态身份验证系统
为什么使用 Jwt 最近,移动开发的劲头越来越足,学校搞的各种比赛都需要用手机 APP 来撑场面,所以,作为写后端的,很有必要改进一下以往的基于 Session 的身份认证方式了,理由如下: 移动端经 ...
随机推荐
- 一步一步带你在VS 2017中配置OpenGL
在VS2017环境中配置OpenGL,我们分三步:配置GLFW.配置GLAD.导出项目模板. 配置GLFW 1.首先下载GLFW,点击这里,进入Github下载. 或者 点击这里从百度云下载,提取码为 ...
- [Python] Python 学习记录(2)
1.range(x,y) [x,y) >>> range(0,4) #0,1,2,3 >>> range(1,4) #1,2,3 2.dics dics.get(k ...
- [WP8.1]RSA 使用BouncyCastle 公钥解密
写应用的时候遇到个服务器返回私钥加密过的数据 ,然后要在客户端用公钥解密的需求 ,一直没找到方法,应用搁置了一个学期,多方搜索,结论就是.net没有实现公钥解密的方法,要自己实现,于是硬着头皮开始看 ...
- SpringBootSecurity学习(20)前后端分离版之OAuth2.0刷新token
刷新token 前面的例子和配置都是从头开始申请授权码和令牌,现在来看一下如何根据获取令牌时,回参中的 refresh_token 来刷新令牌.现在在项目中配置的是内存模式的默认用户名密码,第一步先改 ...
- bat脚本自动安装Jmeter&Jdk
一句话能解决的事情,绝对不要写一篇文章:一篇文章能解决的事情,绝对不要使用各种工具:一个工具能解决的事情,绝对不要跑东跑西…… 文章主要介绍脚本如何下载.安装.配置Jmeter&Jdk. 不多 ...
- .Net Core下使用HtmlAgilityPack解析采集互联网数据
HtmlAgilityPack应该算是.Net下最好用的html解析库了. 因为最近帮朋友采集一些数据,在nuget里面搜索了好几个库,最后决定就用HtmlAgilityPack.并简单的记录下使用的 ...
- display——table-cell属性
display的table和table-cell一般情况下用的不多,所以很少有人去关注它,但他们两个联手起来会给你惊喜! 当两个或者两个以上标签一起使用显示在同一行时,以前常用的是float.posi ...
- 原生js动态创建、获取、删除属性的几种方式
1.创建属性 <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <ti ...
- JVM本地方法栈及native方法
看到虚拟机栈和本地方法栈的区别的时候有点疑惑,因为本地方法栈为虚拟机的Native方法服务.以下转载一篇关于native方法的介绍: http://blog.csdn.net/wike163/arti ...
- [USACO10NOV]奶牛的图片Cow Photographs
题目描述 Farmer John希望给他的N(1<=N<=100,000)只奶牛拍照片,这样他就可以向他的朋友炫耀他的奶牛. 这N只奶牛被标号为1..N. 在照相的那一天,奶牛们排成了一排 ...