[ 菜鸟也玩IoT ] 教你借助Win10手机制作Arduino蓝牙遥控小车
先看下图:
***附件停止解析***
动图演示:
***图片停止解析***
选择Arduino是因为这个上手比较简单,而且Win10商店已经***链接停止解析***(微软商店少有的Win32转制应用之一,您也可以选择用***链接停止解析***来编写)。已经是很入门的套件了。基本上只要你懂点C语言,再懂点初中生级别的电路基础知识就能搞定了。
虽然不是很难,但这里还是以我个人情况,来说一下本教程所涉及的知识难度:
电子元器件知识:都是中学时代自己研究积累的基础知识,很多都忘了,所以得在网易云课堂,搜索几个关于Arduino的基础视频,来顺带的回顾一下。(您也可以直接照搬我下面的电路连接图,唯一要注意的是,正负极千万别搞错!正负极千万别搞错!正负极千万别搞错!)
Arduino部分:C语言是大学时候学的(当时没兴趣全部学完,计算机等级二),基础的编程概念能懂,Arduino开发,也主要是用的C语言。
UWP客户端部分:C#只看过基础教程,没做过项目,UWP手机客户端开发相关的知识都是网上搜索得到的。
这里核心需要了解的是电子元器件知识和C语言知识,UWP开发部分你可以略过,直接用我的源代码。
电路连接图已经献上。。。。有什么问题可以在本贴回复。
***附件停止解析***
因为是实验项目,所以下图接线看起来很糙,请莫怪。
***附件停止解析***
***附件停止解析***
***附件停止解析***
***附件停止解析***
***附件停止解析***
以下是我用的mini版L298N电机驱动模块的引脚说明:(数字口和PWM模拟口可以共用一个)
***附件停止解析***
相关代码已经放到了Github:***链接停止解析***
为防止不可控的内容风险,本站已关闭新用户注册,新贴的发表及评论;
你现在看到的内容只是互联网用户曾经发表的言论快照,仅用于老用户留存纪念,且仅与科技行业相关,全部内容不代表本站观点及立场;
本站重新开放前已针对包括用户隐私、版权保护、信息安全、国家政策在内的各种互联网法律法规要求,执行了隐患内容的自查、屏蔽和删除;
本站目前所属个人主体,未有任何盈利安排与计划,且与原WFUN.COM所属公司不存在任何关联关系;
如果本帖内容或者相关资源侵犯到您的合法权益,或者您认为存在问题,那么请您务必点此举报或投诉!
本帖最后由 机机 于 2016-12-3 23:55 编辑
代码方面我已经帮你写好了,主要复制以下四段代码即可。
如果你懂编程,你可以在我的基础上继续完善,代码都不难(难点也就在C#的蓝牙连接那几句),我这代码应该属于初学者级别。
先附上Arduino上需要写入的代码:
[mw_shl_code=c,true]
//以下数字都是Arduino上的引脚编号,你可以看到合计有四个引脚需要在代码里面用到了,而且每个引脚可以同时设置为数字/模拟端口。
const int kong_in1 = 5; //数字端口 只有开和关 (所谓数字,你可以理解为把物理上的低电平和高电平转换为编程上的0和1两个数值,再简单点理解就是开关,只不过这里的开关在关闭状态下仍然是有电流在流入的。)
const int kong_in2 = 6; //数字端口 只有开和关
const int kong_in3 = 9; //数字端口 只有开和关
const int kong_in4 = 10; //数字端口 只有开和关
const int pwm_in1 = 5; //模拟端口 数值范围0~255(所谓模拟,可以理解为把电子元件的物理值转换为范围为编程上的0~255的数值,比如电机的转速就需要这个来控制)
const int pwm_in2 = 6; //模拟端口
const int pwm_in3 = 9;//模拟端口
const int pwm_in4 = 10;//模拟端口
int l_speed = 0;//定义速度变量,PWM输出范围为0~255
int l_pwm_in;
int r_speed = 0;//定义速度变量,PWM输出范围为0~255
int r_pwm_in;
int gs = 0;
//先初始化,可以理解为Arduino在启动之后需要执行的代码
void setup() {
Serial.begin(115200); // 115200是蓝牙通信过程蓝牙接收器元件需要用到的的端口号,有的蓝牙模块可能和我这个不一样。
pinMode(kong_in1,OUTPUT); //模拟端口默认设置为输出,这个用来控制电机速度
pinMode(kong_in2,OUTPUT);
digitalWrite(kong_in1, LOW); //数字端口默认设置为低电平,也就是先关闭电机
digitalWrite(kong_in2, LOW);
pinMode(kong_in3, OUTPUT);
pinMode(kong_in4, OUTPUT);
digitalWrite(kong_in3, LOW);
digitalWrite(kong_in4, LOW);
}
// Arduino在运行的时候会循环的执行这里面的代码
void loop() {
while (Serial.available())
{
char c = Serial.read();//读取蓝牙接收到的字符串
Serial.println(c);
if (c == 'q') { // 如果给蓝牙串口传一个'q'字符表示左侧轮子前进
l_speed = 150; //先设定一个可以控制转速的模拟数值为150,注意这里只是编程值,不是转速也不是电压值/电流值。
l_pwm_in = pwm_in1; //把这个数值传给1号模拟口
//以下一高一低,就让左侧电机正转
digitalWrite(kong_in1, HIGH); //给1号端口一个高电平
digitalWrite(kong_in2, LOW); //给2号端口一个低电平
}
if (c == 'a') { // 如果给蓝牙串口传一个'a'字符表示左侧轮子后退
l_speed = 150; //先设定一个可以控制转速的模拟数值为150,注意这里只是编程值,不是转速也不是电压值/电流值。
l_pwm_in = pwm_in2; //把这个数值传给2号模拟口
//以下一低一高,就让左侧电机反转
digitalWrite(kong_in1, LOW); //给1号端口一个低电平
digitalWrite(kong_in2, HIGH); //给2号端口一个高电平
}
if (c == 'z') { // 如果给蓝牙串口传一个'z'字符表示左侧轮子停止
//以下表示电机速度为0,而去两个相同电平,表示左侧电机停转
l_speed = 0;
digitalWrite(kong_in1, LOW);
digitalWrite(kong_in2, LOW);
}
//以下解释不多,因为与上面意思差不多
if (c == 'w') { // 如果给蓝牙串口传一个'w'字符表示右侧轮子前进
r_speed = 150;
r_pwm_in = pwm_in3;
digitalWrite(kong_in3, HIGH);
digitalWrite(kong_in4, LOW);
}
if (c == 's') { // 如果给蓝牙串口传一个's'字符表示右侧轮子后退
r_speed = 150;
r_pwm_in = pwm_in4;
digitalWrite(kong_in3, LOW);
digitalWrite(kong_in4, HIGH);
}
if (c == 'x') { // 如果给蓝牙串口传一个'x'字符表示右侧轮子停止
r_speed = 0;
digitalWrite(kong_in3, LOW);
digitalWrite(kong_in4, LOW);
}
}
//左侧轮子开始转动
if (l_speed > 0) { //速度为0时开始加速
l_speed += 10; //因为在循环里面,所以这里表示随时间加速
}
if (l_speed > 250) { //速度达到最大值250则不要再加了,防止电机发热。
l_speed = 250;
}
//右侧轮子开始转动
if (r_speed > 0) {
r_speed += 10;
}
if (r_speed > 250) {
r_speed = 250;
}
delay(100); // 循环一次停100毫秒,因为循环太快的话,加速效果看不到了。
if (l_pwm_in) { //结合上面代码,这里用来判断是前进还是后退
analogWrite(l_pwm_in, l_speed); // 模拟端口向左边电机输出速度值
}
if (r_pwm_in) { //结合上面代码,这里用来判断是前进还是后退
analogWrite(r_pwm_in, r_speed); // 模拟端口向右边电机输出速度值
}
}
[/mw_shl_code]
你可能有点疑惑,为啥要用 q,w,a,s,z,x 这6个字符,注意看下图,相信你能懂:
***附件停止解析***
本帖最后由 机机 于 2016-12-4 10:12 编辑
继续UWP客户端代码:
MainPage.xaml 代码:
[mw_shl_code=xml,true]<Page
x:Class="Arduino_bluetooth.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Arduino_bluetooth"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="80" />
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Button x:Name="btn_button_Click" Grid.Row="0" Grid.Column="0" Content="搜索设备" HorizontalAlignment="Center" VerticalAlignment="Center" Click="button_Click" Width="150" Height="32" Grid.ColumnSpan="2" />
<Button x:Name="btn_button_close" Grid.Row="0" Content="关闭" HorizontalAlignment="Center" VerticalAlignment="Center" Click="button_close" Height="32" Width="50" Grid.Column="3" />
<Button x:Name="btn_click_info" Grid.Column="4" Content="状态" HorizontalAlignment="Center" VerticalAlignment="Center" Click="click_info" Height="32" Width="50" Background="Red" Foreground="White" />
<ListView x:Name="resultsListView" Grid.Row="0" Grid.Column="2" SelectionChanged="resultsListView_SelectionChanged" Margin="0,24">
</ListView>
<!-- 注意我这里绑定的是Holding事件,只能触摸操作,不支持鼠标,要用鼠标,就得绑定到Click事件。-->
<Button x:Name="btn_clickLeftTOP" Grid.Row="1" Content="左前进" HorizontalAlignment="Center" VerticalAlignment="Center" Height="75" Width="150" Grid.ColumnSpan="2" Holding="LeftTOP_Holding" />
<Button x:Name="btn_clickLeftBOTTOM" Grid.Row="2" Content="左后退" HorizontalAlignment="Center" VerticalAlignment="Center" Height="75" Width="150" Grid.ColumnSpan="2" Holding="LeftBOTTOM_Holding" />
<Button x:Name="btn_clickRightTOP" Grid.Row="1" Content="右前进" HorizontalAlignment="Center" VerticalAlignment="Center" Height="75" Width="150" Grid.ColumnSpan="2" Holding="RightTOP_Holding" Grid.Column="3" />
<Button x:Name="btn_clickRightBOTTOM" Grid.Row="2" Content="右后退" HorizontalAlignment="Center" VerticalAlignment="Center" Height="75" Width="150" Grid.ColumnSpan="2" Holding="RightBOTTOM_Holding" Grid.Column="3" />
</Grid>
</Page>
[/mw_shl_code]
MainPage.cs 代码:
[mw_shl_code=csharp,true]
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Devices.Bluetooth;
using Windows.Devices.Bluetooth.Rfcomm;
using Windows.Devices.Enumeration;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.Networking.Sockets;
using Windows.Storage.Streams;
using Windows.System;
using Windows.UI.Popups;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;
//“空白页”项模板在 ***链接停止解析*** 上有介绍
namespace Arduino_bluetooth
{
/// <summary>
/// 可用于自身或导航至 Frame 内部的空白页。
/// </summary>
public sealed partial class MainPage : Page
{
private RfcommDeviceService service;
private DeviceInformationCollection services;
private StreamSocket socket;
private DataWriter writer;
private DataReader reader;
private string deviceName;
public MainPage()
{
this.InitializeComponent();
var action = Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, new Windows.UI.Core.DispatchedHandler(() => {
}));
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
bluetooth_list(); //进入APP立即搜索设备
base.OnNavigatedTo(e);
}
private void button_Click(object sender, RoutedEventArgs e)
{
if (services == null || services.Count == 0)
{
bluetooth_list(); //先搜索设备
}else if(service == null)
{
bluetooth_connect(); //如果有搜索到设备,则连接选中的设备
}
}
/// <summary>
/// 执行蓝牙设备搜索。
/// </summary>
public async void bluetooth_list()
{
services = await DeviceInformation.FindAllAsync(RfcommDeviceService.GetDeviceSelector(RfcommServiceId.SerialPort));
//获取周围所有的蓝牙串口设备
foreach (DeviceInformation v in services)
{
resultsListView.Items.Add(v.Name);
//把找到的设备名称添加到前端XAML界面的ListView里面去
}
if (services == null || services.Count == 0)
{
bluetooth_check();
}
else
{
btn_button_Click.Content = "继续连接";
OnConnectionEstablished(true, false); //让连接按钮可点击
btn_click_info.Background = new SolidColorBrush(Windows.UI.Colors.Blue);
}
}
private void resultsListView_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
foreach(string Item in e.AddedItems)
{
deviceName = Item;
}
}
/// <summary>
/// 开始连接蓝牙串口设备
/// </summary>
private async void bluetooth_connect()
{
if (deviceName == null)
{
await new MessageDialog("请您先从搜索到的蓝牙设备列表选择一个设备").ShowAsync();
return;
}
OnConnectionEstablished(false, false); //点击连接按钮后立即禁用连接按钮,防止误操作
services = await DeviceInformation.FindAllAsync(RfcommDeviceService.GetDeviceSelector(RfcommServiceId.SerialPort)); //这里再扫描一次设备,是确保设备没有掉线。
if (services.Count > 0)
{
foreach (DeviceInformation info in services)
{
if (deviceName==info.Name)
{
service = await RfcommDeviceService.FromIdAsync(info.Id);
//调试代码
//Debug.WriteLine(service.DeviceAccessInformation.CurrentStatus.ToString());
//确保设备有权访问再连接,否则会遭遇闪退!
if (service.DeviceAccessInformation.CurrentStatus.ToString() != null && service.DeviceAccessInformation.CurrentStatus.ToString() == "Allowed")
{
socket = new StreamSocket();
await socket.ConnectAsync(service.ConnectionHostName, service.ConnectionServiceName);
writer = new DataWriter(socket.OutputStream);
reader = new DataReader(socket.InputStream);
OnConnectionEstablished(false, true); //连接成功后继续禁用连接按钮,但开启控制按钮。
btn_button_Click.Content = "连接成功!";
btn_click_info.Background = new SolidColorBrush(Windows.UI.Colors.LawnGreen);
}
else
{
bluetooth_check();
return;
}
}
}
}
else
{
bluetooth_check();
return;
}
}
/// <summary>
/// 断开蓝牙连接
/// </summary>
public void bluetooth_close()
{
if (writer != null)
{
writer.DetachStream();
writer.Dispose();
writer = null;
}
if (socket != null)
{
socket.Dispose();
socket = null;
}
if (service != null)
{
service.Dispose();
service = null;
}
//断开后让连接按钮可点击
OnConnectionEstablished(true, false);
btn_button_Click.Content = "继续连接";
btn_click_info.Background = new SolidColorBrush(Windows.UI.Colors.Blue);
}
/// <summary>
/// 连接按钮状态,因为在已经连接成功的情况下重复点击连接会出错闪退
/// </summary>
/// <param name="off">不可点击</param>
/// <param name="on">可点击</param>
private void OnConnectionEstablished(bool off, bool on)
{
var action = Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, new Windows.UI.Core.DispatchedHandler(() => {
btn_button_Click.IsEnabled = off;
btn_clickLeftTOP.IsEnabled = on;
btn_clickLeftBOTTOM.IsEnabled = on;
btn_clickRightTOP.IsEnabled = on;
btn_clickRightBOTTOM.IsEnabled = on;
}));
}
/// <summary>
/// Win10里面的蓝牙串口长时间不交换数据会掉线
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private async void click_info(object sender, RoutedEventArgs e)
{
await Launcher.LaunchUriAsync(new Uri("ms-settings:bluetooth"));
}
private async void bluetooth_check()
{
var message = new MessageDialog("搜索设备失败,请确保您的蓝牙串口设备已经开启连接");
message.Commands.Add(new UICommand("确认"));
message.Commands.Add(new UICommand("取消"));
var result = await message.ShowAsync();
if (result.Label == "确认")
{
await Launcher.LaunchUriAsync(new Uri("ms-settings:bluetooth"));
}
}
private void button_close(object sender, RoutedEventArgs e)
{
bluetooth_close();
}
/// <summary>
/// 左侧前进按钮
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private async void LeftTOP_Holding(object sender, HoldingRoutedEventArgs e) {
//注意我这里绑定的是Holding事件,只能触摸操作,不支持鼠标,要用鼠标,就得绑定到Click事件。
//以下代码可以实现手指长按前进,移开就停止的操作。
//如果按钮按下,则给串口设备发送一个字符"q",左侧马达开始正转。
if(e.HoldingState == Windows.UI.Input.HoldingState.Started){
writer.WriteString("q");
await writer.StoreAsync();
}else{ //如果按钮没有按下,比如释放掉,则给串口设备发送一个字符"z",左侧马达停止转动。
writer.WriteString("z");
await writer.StoreAsync();
}
}
/// <summary>
/// 左侧后退按钮
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private async void LeftBOTTOM_Holding(object sender, HoldingRoutedEventArgs e)
{
if (e.HoldingState == Windows.UI.Input.HoldingState.Started)
{
writer.WriteString("a");
await writer.StoreAsync();
}
else
{
writer.WriteString("z");
await writer.StoreAsync();
}
}
/// <summary>
/// 右侧前进按钮
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private async void RightTOP_Holding(object sender, HoldingRoutedEventArgs e)
{
if (e.HoldingState == Windows.UI.Input.HoldingState.Started)
{
writer.WriteString("w");
await writer.StoreAsync();
}
else
{
writer.WriteString("x");
await writer.StoreAsync();
}
}
/// <summary>
/// 右侧后退按钮
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private async void RightBOTTOM_Holding(object sender, HoldingRoutedEventArgs e)
{
if (e.HoldingState == Windows.UI.Input.HoldingState.Started)
{
writer.WriteString("s");
await writer.StoreAsync();
}
else
{
writer.WriteString("x");
await writer.StoreAsync();
}
}
}
}
[/mw_shl_code]
对了,别忘了给你的UWP配置好权限,否则你是不能调用手机的蓝牙接口的:
配置文件就是Package.appxmanifest
关键在下面这一段,写到</Applications>的后面
Package.appxmanifest:
[mw_shl_code=xml,true]
<Capabilities>
<Capability Name="internetClient" />
<Capability Name="internetClientServer" />
<DeviceCapability Name="bluetooth" />
<DeviceCapability Name="bluetooth.rfcomm">
<Device Id="any">
<Function Type="name:serialPort" />
</Device>
</DeviceCapability>
</Capabilities>
[/mw_shl_code]
最后附上,我这整个项目用到的一些软件工具,大部分都在微软官方商店有下载:
[ IDE ]
Arduino IDE 官方UWP版(Win32转制,叼的不要不要的):
***链接停止解析***
Arduino IDE For Visual Studio 插件版:
***链接停止解析***
以上二选一,我推荐后者,有代码提示。
[ C#编写 ]
然后是C#编程,这就不说了 Visual Studio 必须的:
***链接停止解析***
[ 蓝牙串口调试工具 ]
商店里面搜索"蓝牙串口",有很多。
比如,蓝牙串口调试助手(UWP免费试用版足够调试了):***链接停止解析***
调试工具的作用,就是确保你的蓝牙模块和手机之间可以正常通信,比如用调试工具给你的蓝牙串口设备发送字符串,看看蓝牙串口设备能不能响应。
Quote机机 发表于 2016-12-4 10:03
常规版也可以啊,就是大了点。
毕竟我只有大的,还有一个我自己焊的
Quote***链接停止解析***
毕竟我只有大的,还有一个我自己焊的
完全可以用,一般都用L298N作为电机驱动。
我这个mini版淘宝买的,不到4块钱。
Quote***链接停止解析***
大神,我也写了一个蓝牙串口的通信程序,但是现在无法识别对方断开连接,请问怎么才能知道对方断开了连接呢 ...
设备是不会主动告诉你它掉线了的,所以只能自己去搜索。
我的代码里面有,就是遍历一下所有串口蓝牙设备,找不到就表示掉线了。
你好,楼主。我准备将你的这个作为我的毕业设计参考方案,我想问一下,这个arduino开发套件有没有合适的店家可以推荐一下?不是太了解这个行情和哪家比较靠谱。谢谢楼主