使用 Firmata 和 Xbox One 控制器控制 Arduino Rover
组件和用品
| × | 1 | ||||
| × | 1 | ||||
| × | 1 |
应用和在线服务
|
关于这个项目
几个月前,我以非常优惠的价格买了一个小漫游车(由 Arduino Uno 控制)。该套件非常完整:汽车底盘、2 个车轮、2 个直流齿轮电机、一个 UNO R3、一个 L298N 双 H 桥电机控制器和其他几个组件。
该漫游车旨在为自主操作编程。因此,套件中还添加了超声波传感器和伺服系统。套件中还有一个不错的 Arduino 传感器扩展板 5。是的,真的很划算;-)
但我的想法是同时使用 Xbox One 控制器和 Firmata 协议来由我自己或我的一个儿子来驱动这个控制器。而且效果很好!
这是最终解决方案的视频:
本项目中使用的主要部分是:
- 漫游车套件(我们仅在本次演示中使用部分部件:底板、轮子、电机、Arduino Uno、9 伏电池座和 L298N 双 H 桥电机控制器)莉>
- 一个可容纳 6 节 AA 电池的额外电池座
- 蓝牙模块 (HC-06)
- 带有 Windows 无线适配器的 Xbox One 控制器(用于将其连接到笔记本电脑)
- 一台笔记本电脑(装有 Windows 10 + VS2015 + 蓝牙适配器)
- 仅用于戏剧效果的闪光灯
构建工具包
构建套件并不难。虽然施工手册是中文的,但这一切似乎都很合乎逻辑。只有一个底板,所以我不得不在上面放一些组件,在底部放一些组件(现在是电机控制器):
在这张图片中添加了两个不同的电池座。在编程过程中,我发现我必须使用单独的电源才能让电机运行。
连接电机——控制器的引脚布局
这次硬件的重点不是 Arduino Uno,而是电机控制器。
“L298 是用于直流有刷电机和步进电机的双 H 桥驱动器。它支持较宽的工作电压范围,并且可以在通孔封装中为每个通道提供 2 A 电流,适合 DIY 项目。”
该控制器将使用来自 Arduino 的信号控制两个电机中每一个的速度和方向。
L298N 双 H 桥电机控制器的引脚布局为:
- 加+直流电机1
- 减去 - 直流电机 1
- 电源输入。我从单独的电池座提供 9 伏电压
- 共同点。连接到我单独的电池座和 Arduino
- 断电 .可以产生 5 伏电压(未使用 .我的 Arduino 从另一个电池座获得电源)
- ENA 连接到 Arduino D10。此端口支持 PWM(白色)
- IN1 连接到 Arduino D9。 (灰色)
- IN2 连接到 Arduino D8。 (紫色)
- IN3 连接到 Arduino D7。 (蓝色)
- IN4 连接到 Arduino D6。 (绿色)
- ENB 连接到 Arduino D5。此端口支持 PWM(黄色)
- 加+直流电机2
- 减去 - 直流电机 2
- 2 和 3 旁边的跳线 未删除 因为我没有超过输入功率超过 12 伏(最高 35 伏)(图中未标出跳线)
注:控制器自带电源。 Arduino 也有一个。为了让事情保持运行(不要吹薄,给他们一个共同点 .见上面的第 4 项)
连接电机——Arduino 的引脚布局
连接 Arduino 非常简单。我们将接地和 5 伏电源连接到 Arduino。我们将六根线(ENA、IN1-4 和 ENB)连接到引脚 D10 到 D5。
必须清楚的是,端口 7 和 8(以及另一个电机的 9 和 10)只是常规的 GPIO 端口。这些将用于电机的方向。如果两个端口(例如 7 和 8)都是低电平,电机将什么也不做(停止)。如果一个为高,另一个为低,电机将向一个方向运行。如果以相反的方式连接(第一个设置为 LOW,另一个设置为 HIGH),则连接的电机将向另一个方向运行。
但是..只是设置这些引脚不会产生任何结果。还没有活动部件!
神奇之处在于引脚 5 和 10。这些是“特殊”引脚,可以生成 PWM 信号。设置 0 到 255 之间的值将导致电机运行非常缓慢(停止)或高速运行。
注意:Arduino Uno 上支持 PWM 的每个端口都标有波浪号(~)。
连接蓝牙
我使用的蓝牙模块只需要连接到 RX 和 TX 端口(跨线),它需要来自 Arduino 的 5 伏电源和地。
Arduino 上的Firmata 草图
Firmata 草图“StandardFirmata”就是我们在 Arduino 上所需要的!只需从 Arduino IDE 示例中获取并上传即可(可能您需要先取消 TX/RX 才能完成上传)。
注意:由于我的蓝牙模块质量不足,我总是降低草图中硬编码的波特率,并将其设置为9600。
先生们,启动你的引擎
或者,测试连接...
那我为什么要使用 Firmata?因为这很容易。有多容易?很简单。它甚至可以在没有编程的情况下完成。只需启动 Windows Remote Arduino Experience 应用程序(可在商店购买,也可在 Windows 10 移动设备上运行)。
首先,您必须连接到已配对的 HC-6 蓝牙模块。
配对后,转到 PWM 页面。启用数字引脚 5 并为其赋值,例如 128。
当心:下一步将使您的电机运转。戴上头盔,滚下面粉。当你看到它时你就会知道为什么。
然后转到数字页面。并拨动数字引脚 6 的开关。
现在,如果一切都已连接并正在运行,其中一个电机就会旋转!
如果是这样,您可以检查其他速度(使用更高或更低的 PWM 值)或更改方向(将数字引脚 6 设置为 0 伏,将数字引脚 7 设置为 5 伏)。
你有它。你可以控制那个轮子!
但还有更多,同样适用于引脚 10 (PWM) 和数字引脚 9 和数字引脚 8。
两个轮子都在跑。现在开始编码……
UWP 应用作为媒人
必须明确的是,我们需要在 Xbox One 控制器和流动站之间安装一个新的 UWP 应用。
我已经在这里发表了关于使用 Xbox One 控制器的博客。这次我们将把这些知识与漫游车结合起来。
让我们看看我们的 UWP 应用的界面:
它非常乏味,两个按钮和一个文本块。我将无事可做,然后首先使用 Firmata 连接到 Arduino。一旦建立连接,控制器按钮将不得不启动一个巨大的循环来读取控制器输入并将其转换为有用的命令。
而我们想要创造的是这个经典的带有两个手柄的坦克控制器。我们只需要检查 Xbox One 控制器上的两个拇指杆。向前和向后移动它们也会启动相应的电机向前和向后:
注意:如果您启动新的 UWP 应用,请不要忘记添加蓝牙功能。您必须为 Firmata 安装 nuget 包。
首先,我们在主表单网格中添加 XAML 代码段(请参阅 Xaml 源代码的代码部分),这样我们就有了一些按钮。
接下来我们添加主窗体的代码(参见 C# 源代码的代码部分)。我基本上分为两部分。首先,我们使用 Firmata 协议通过蓝牙建立连接。接下来我们开始在 Xbox One 控制器上监听输入。
在检查控件的循环中,我们决定拇指杆是向前或向后指向还是在“停止”范围内。之后我们决定PWM引脚的值。
有趣的是,写入 PWM 值在 Firmata 中表示为将模拟值写入数字端口。 Arduino类中没有特殊的PWM方法。
我添加了两种方法“ArduinoDigitalWrite”和“ArduinoAnalogWrite”,以防止反复向Arduino写入相同的值。这会干扰通信并降低 Arduino 的性能。 (在一个更丰富的设计中,我添加了一个蜂鸣器。在不忽略重复命令的情况下,蜂鸣器的性能非常差,而且很难听到。
当电机得到低 PMW 脉冲时,电机不会直接开始运行,它会发出非常独特的声音。这是正常行为。这就是我发现我必须添加两个电源的原因。我第一次连接所有东西时,我以为控制器坏了。没啥事儿。直到我拔掉其中一个电机的插头,另一个开始发出呜呜声。
这就是它的工作原理:
脚注:闪光灯不仅仅是为了戏剧效果。每当您的作品开始移动时,请注意安全。这个装置是愚蠢的,你不是!所以我一开始就把这盏灯放在了上面。
代码
- 用于启动通信的 Xaml 控件
- UWP 应用主窗体的代码隐藏
Xaml 控件开始通信snippets
将此粘贴到新 UWP 应用的主窗体中UWP 应用程序主窗体的代码隐藏C#
此代码使用 Firmata 连接到漫游车并传递 Xbox One 控制器命令public 密封部分类 MainPage :Page{ private BluetoothSerial _bluetooth;私有远程设备_arduino;私人 UwpFirmata _firmata =null;私人游戏手柄_Gamepad =null; // 我们从不提供超过 255 * 0,75 的 PWM private double _SpeedLimit =0.75; // 低于这个绝对值的值会停止流动站 private double _ActionPoint =0.15;私有字节_LeftForward =6;私有字节_LeftBackward =7;私有字节_LeftValue =5;私有字节_RightForward =9;私有字节_RightBackward =8;私有字节_RightValue =10; private Dictionary_CurrentPinStates =new Dictionary (); private Dictionary _CurrentSpeedValues =new Dictionary (); public MainPage() { this.InitializeComponent(); } private void btnStart_Click(object sender, RoutedEventArgs e) { btnStart.IsEnabled =false; Gamepad.GamepadAdded +=Gamepad_GamepadAdded; Gamepad.GamepadRemoved +=Gamepad_GamepadRemoved; _bluetooth =new BluetoothSerial("HC-06"); _bluetooth.ConnectionLost +=BluetoothConnectionLost; _bluetooth.ConnectionFailed +=BluetoothConnectionFailed; _bluetooth.ConnectionEstablished +=OnConnectionEstablished; _firmata =新的 UwpFirmata(); _arduino =new RemoteDevice(_firmata); _firmata.begin(_bluetooth); _bluetooth.begin(9600, SerialConfig.SERIAL_8N1); _arduino.DeviceReady +=ArduinoDeviceReady; } private async void BluetoothConnectionLost(string message) { await Dispatcher.RunAsync( CoreDispatcherPriority.Normal, () => { tbRead.Text ="ConnectionLost" + message; btnStart.IsEnabled =true; }); } private async void BluetoothConnectionFailed(string message) { await Dispatcher.RunAsync( CoreDispatcherPriority.Normal, () => { tbRead.Text ="ConnectionFailed" + message; btnStart.IsEnabled =true; }); } private async void ArduinoDeviceReady() { await Dispatcher.RunAsync( CoreDispatcherPriority.Normal, () => { tbRead.Text ="Device Ready"; }); } private void OnConnectionEstablished() { var action =Dispatcher.RunAsync(CoreDispatcherPriority.Normal, new DispatchedHandler(() => { DisableEnableButtons(true); ArduinoDigitalWrite(_LeftForward, PinState.LOW); ArduinoDigitalWrite(_LeftBackward, PinState.LOW); ArduinoDigitalWrite (_RightBackward, PinState.LOW); ArduinoDigitalWrite(_RightForward, PinState.LOW); })); } private void DisableEnableButtons(bool enabled) { // 禁用所有按钮。否则 // 'A' 将按下聚焦的那个。 btnController.IsEnabled =启用; btnStart.IsEnabled =启用; } private async void Gamepad_GamepadRemoved(object sender, Gamepad e) { _Gamepad =null; await Dispatcher.RunAsync( CoreDispatcherPriority.Normal, () => { tbRead.Text ="控制器移除"; }); } private async void Gamepad_GamepadAdded(object sender, Gamepad e) { _Gamepad =e; await Dispatcher.RunAsync( CoreDispatcherPriority.Normal, () => { tbRead.Text ="Controller added"; }); } private async void btnController_Click(object sender, RoutedEventArgs e) { DisableEnableButtons(false); while (true) { await Task.Delay(TimeSpan.FromMilliseconds(3)); if (_Gamepad ==null) { 继续; } // 获取当前状态 var reading =_Gamepad.GetCurrentReading();如果 (Math.Abs(reading.LeftThumbstickY) <_ActionPoint) { ArduinoDigitalWrite(_LeftForward, PinState.LOW); ArduinoDigitalWrite(_LeftBackward, PinState.LOW); sldrLeftSpeed.Value =0; } else if (reading.LeftThumbstickY>=_ActionPoint) { ArduinoDigitalWrite(_LeftForward, PinState.HIGH); ArduinoDigitalWrite(_LeftBackward, PinState.LOW); sldrLeftSpeed.Value =255 * Math.Abs(reading.LeftThumbstickY) * _SpeedLimit; } else if (reading.LeftThumbstickY <=-_ActionPoint) { ArduinoDigitalWrite(_LeftForward, PinState.LOW); ArduinoDigitalWrite(_LeftBackward, PinState.HIGH); sldrLeftSpeed.Value =255 * Math.Abs(reading.LeftThumbstickY) * _SpeedLimit; } if (Math.Abs(reading.RightThumbstickY) <_ActionPoint) { ArduinoDigitalWrite(_RightForward, PinState.LOW); ArduinoDigitalWrite(_RightBackward, PinState.LOW); sldrRightSpeed.Value =0; } else if (reading.RightThumbstickY>=_ActionPoint) { ArduinoDigitalWrite(_RightForward, PinState.HIGH); ArduinoDigitalWrite(_RightBackward, PinState.LOW); sldrRightSpeed.Value =255 * Math.Abs(reading.RightThumbstickY) * _SpeedLimit; } else if (reading.RightThumbstickY <=-_ActionPoint) { ArduinoDigitalWrite(_RightForward, PinState.LOW); ArduinoDigitalWrite(_RightBackward, PinState.HIGH); sldrRightSpeed.Value =255 * Math.Abs(reading.RightThumbstickY) * _SpeedLimit; } } } private void ArduinoDigitalWrite(byte port, PinState state) { // 忽略重复的命令 var exists =_CurrentPinStates.ContainsKey(port); if (!exists || _CurrentPinStates[port] !=state) { _CurrentPinStates[port] =state; _arduino.digitalWrite(端口,状态); } } private void ArduinoAnalogWrite(byte port, ushort value) { // 忽略重复的命令 var exists =_CurrentSpeedValues.ContainsKey(port); if (!exists || _CurrentSpeedValues[port] !=value) { _CurrentSpeedValues[port] =value; _arduino.analogWrite(端口,值); } }}
制造工艺
- 使用 Arduino 和 RFID 和 Python 的考勤系统
- 使用 Arduino、1Sheeld 和 Android 的通用远程控制
- 使用 Arduino 和智能手机的 DIY 电压表
- 使用物联网远程控制机械臂
- 使用 Arduino 的频率和占空比测量
- Sonar 使用 arduino 并在处理 IDE 上显示
- 使用 Bolt 和 Arduino 控制 LED 亮度
- 使用 Arduino 的简单智能机械臂
- 使用 Alexa 和 Arduino IoT Cloud 完全控制您的电视
- 使用 Arduino 和 RDA8057M 的 FM 收音机
- 使用智能手机控制您的灯光系统
- 漏水探测器和阀门控制