儿子最爱车车,给他做一个。
遥控小车也是互联网上资料最丰富的一个电子DIY项目类型,然后其综合性和复杂度算得上是对入门的一个检验。最重要的是,我儿子(虽然才两岁)对车车是真爱啊。与其商场里买一堆重复的车车,不如自己动手,打造自己的梦幻之车。我这算是给他先探探路了。
最终成品先放上来,这是一个支持红外遥控的小车车。要做这么一个车车,需要准备的东西不少:
- TT直流电机四驱底盘套装:淘宝上买的,不到30,包含了四个电机和轮子,还有亚克力底盘。还有一些电源线,固定螺丝之类的。
- 直流电机驱动模块:选择了大家用得最多的 293,两路的即可
- 12V,6000 mAh大容量锂电池,解决续航焦虑
- Arduino UNO 主板套装,咸鱼上买的二手的,还包含了红外接收器和遥控器以及其他常用的各种模块
- 电源转接头和一进二出接线器若干
- 快递纸盒子备用,还有双面胶,纳米胶什么的,辅助固定,还有螺丝刀,尖嘴钳之类的工具。
东西陆陆续续到货,底盘的组装就折腾了一个多小时。以后记得最好找店家要详细的说明书,不然涉及到这种结构性的组装,复杂程度会随着零部件的数量增加而指数增长。
然后需要注意的是,我买的这个套装里,电机是没有焊接电源线出来的,这里我还对照着B站教程学习焊锡,虽然跟焊物大帝的距离还有很遥远,但也掌握了基本的方法:1)注意去除导线和触电的氧化层,否则不沾锡;2)先导线浸没镀锡,再到触点端子上焊锡,一下就好,不要拖泥带水。
安装固定好四个轮子之后,可以指定一个车的方向,然后逐个轮子根据这个方向来确定正负极,再然后把同侧的两个轮子作为一组,分别合并正极和负极,插入到驱动模块的OUT端子中,螺丝刀拧紧。
再然后就是把 12V 电池的正极接入到驱动板的12V的输入端,负极接GND。Arduino UNO 控制板各种连线连好,然后就写代码了。
#include <SoftwareSerial.h>
int RX = 0;
// arduino主板上的读取引脚
int TX = 1;
// 主板上的发送引脚,跟蓝牙模块的RXD引脚连接
SoftwareSerial bluetoothSerial(RX, TX);
// 定义软串口接收和发送引脚
int L1 = 2;
int L2 = 3;
int R1 = 4;
int R2 = 5;
void setup() {
// put your setup code here, to run once:
Serial.begin(9600);
bluetoothSerial.begin(9600); // 初始化蓝牙通信
pinMode(L1, OUTPUT);
pinMode(L2, OUTPUT);
pinMode(R1, OUTPUT);
pinMode(R2, OUTPUT);
}
void loop() {
//put your main code here, to run repeatedly:
if (bluetoothSerial.available()){
// 手机上装一下蓝牙调试工具
// 蓝牙模块名称为 JDY-16
char cmd = bluetoothSerial.read();
Serial.print(cmd);
switch (cmd){
case 'w':
forward();
break;
case 's':
backward();
break;
case 'a':
left();
break;
case 'd':
right();
break;
default:
stop();
}
delay(50);
}
}
void forward(){
digitalWrite(L1, HIGH);
digitalWrite(L2, LOW);
digitalWrite(R1, HIGH);
digitalWrite(R2, LOW);
}
void backward(){
digitalWrite(L2, HIGH);
digitalWrite(L1, LOW);
digitalWrite(R2, HIGH);
digitalWrite(R1, LOW);
}
void right(){
digitalWrite(L2, HIGH);
digitalWrite(L1, LOW);
digitalWrite(R1, HIGH);
digitalWrite(R2, LOW);
}
void left(){
digitalWrite(L1, HIGH);
digitalWrite(L2, LOW);
digitalWrite(R2, HIGH);
digitalWrite(R1, LOW);
}
void stop(){
digitalWrite(L1, LOW);
digitalWrite(L2, LOW);
digitalWrite(R1, LOW);
digitalWrite(R2, LOW);
}
手机上安装了蓝牙调试的app,连接上这个 JDY-16 的蓝牙,然后设置一些按钮快捷发送命令,例如 up = "wx",按下这个按钮就等于执行了一次 前进 的命令。但是蓝牙得用手机操作,已有的游戏手柄和蓝牙模块的连接还不知道怎么弄,网上靠谱教程较少,所以还是使用更简单的红外遥控,小朋友也更容易操作。
使用红外模块,需要下载对应的 library。不知道怎么回事,在 arduino IDE 中直接下载总是失败,看报错基本是 github连不上,干脆自己通过别的方法下载后,解压到 arduino
的 libraries 目录下。
然后重新打开 arduino IDE,查找这个 IRremote的示例,选择了 TinyIRReceiver 进行测试和修改。需要注意的是,这个包默认的红外信号引脚是 2,所以我把之前控制直流电机的四个引脚移动到 3-6。然后红外信号不同的按键对应不同的十六进制数值,这个可以在调试的时候在串口查看。我这里得到:
- 下:0x52
- 上:0x18
- 左:0x8
- 右:0x5A
- OK: 0x1C
其他都基本插入到示例代码中指定部位即可完成。完整代码如下:
#include <Arduino.h>
#include "PinDefinitionsAndMore.h"
#include "TinyIRReceiver.hpp"
#if !defined(STR_HELPER)
#define STR_HELPER(x) #x
#define STR(x) STR_HELPER(x)
#endif
int L1 = 3;
int L2 = 4;
int R1 = 5;
int R2 = 6;
void setup() {
Serial.begin(115200);
pinMode(L1, OUTPUT);
pinMode(L2, OUTPUT);
pinMode(R1, OUTPUT);
pinMode(R2, OUTPUT);
while (!Serial)
; // Wait for Serial to become available. Is optimized away for some cores.
#if defined(__AVR_ATmega32U4__) || defined(SERIAL_PORT_USBVIRTUAL) || defined(SERIAL_USB) /*stm32duino*/|| defined(USBCON) /*STM32_stm32*/ \
|| defined(SERIALUSB_PID) || defined(ARDUINO_ARCH_RP2040) || defined(ARDUINO_attiny3217)
delay(4000); // To be able to connect Serial monitor after reset or power up and before first print out. Do not wait for an attached Serial Monitor!
#endif
// Just to know which program is running on my Arduino
#if defined(ESP8266) || defined(ESP32)
Serial.println();
#endif
Serial.println(F("START " __FILE__ " from " __DATE__ "\r\nUsing library version " VERSION_TINYIR));
// Enables the interrupt generation on change of IR input signal
if (!initPCIInterruptForTinyReceiver()) {
Serial.println(F("No interrupt available for pin " STR(IR_RECEIVE_PIN))); // optimized out by the compiler, if not required :-)
}
#if defined(USE_FAST_PROTOCOL)
Serial.println(F("Ready to receive Fast IR signals at pin " STR(IR_RECEIVE_PIN)));
#else
Serial.println(F("Ready to receive NEC IR signals at pin " STR(IR_RECEIVE_PIN)));
#endif
}
void loop() {
if (TinyIRReceiverData.justWritten) {
TinyIRReceiverData.justWritten = false;
#if !defined(USE_FAST_PROTOCOL)
// We have no address at FAST protocol
Serial.print(F("Address=0x"));
Serial.print(TinyIRReceiverData.Address, HEX);
Serial.print(' ');
#endif
Serial.print(F("Command=0x"));
Serial.print(TinyIRReceiverData.Command, HEX);
if (TinyIRReceiverData.Flags == IRDATA_FLAGS_IS_REPEAT) {
Serial.print(F(" Repeat"));
}
if (TinyIRReceiverData.Flags == IRDATA_FLAGS_PARITY_FAILED) {
Serial.print(F(" Parity failed"));
#if !defined(USE_EXTENDED_NEC_PROTOCOL) && !defined(USE_ONKYO_PROTOCOL)
Serial.print(F(", try USE_EXTENDED_NEC_PROTOCOL or USE_ONKYO_PROTOCOL"));
#endif
}
Serial.println();
}
/*
* Put your code here
*/
switch (TinyIRReceiverData.Command){
case 0x18:
Serial.println("Up");
forward();
TinyIRReceiverData.Command = 0x1c;
//重置信号,否则一直保持 up 输出。
break;
case 0x52:
Serial.println("Down");
backward();
TinyIRReceiverData.Command = 0x1c;
break;
case 0x8:
Serial.println("Left");
left();
TinyIRReceiverData.Command = 0x1c;
break;
case 0x5A:
Serial.println("Right");
right();
TinyIRReceiverData.Command = 0x1c;
break;
default:
// Serial.println("Stop");
stop();
}
delay(50);
}
/*
* Optional code, if you require a callback
*/
#if defined(USE_CALLBACK_FOR_TINY_RECEIVER)
/*
* This is the function, which is called if a complete frame was received
* It runs in an ISR context with interrupts enabled, so functions like delay() etc. should work here
*/
# if defined(ESP8266) || defined(ESP32)
IRAM_ATTR
# endif
void handleReceivedTinyIRData() {
# if defined(ARDUINO_ARCH_MBED) || defined(ESP32)
/*
* Printing is not allowed in ISR context for any kind of RTOS, so we use the slihjtly more complex,
* but recommended way for handling a callback :-). Copy data for main loop.
* For Mbed we get a kernel panic and "Error Message: Semaphore: 0x0, Not allowed in ISR context" for Serial.print()
* for ESP32 we get a "Guru Meditation Error: Core 1 panic'ed" (we also have an RTOS running!)
*/
// Do something useful here...
# else
// As an example, print very short output, since we are in an interrupt context and do not want to miss the next interrupts of the repeats coming soon
printTinyReceiverResultMinimal(&Serial);
# endif
}
#endif
void forward(){
digitalWrite(L1, HIGH);
digitalWrite(L2, LOW);
digitalWrite(R1, HIGH);
digitalWrite(R2, LOW);
}
void backward(){
digitalWrite(L2, HIGH);
digitalWrite(L1, LOW);
digitalWrite(R2, HIGH);
digitalWrite(R1, LOW);
}
void right(){
digitalWrite(L2, HIGH);
digitalWrite(L1, LOW);
digitalWrite(R1, HIGH);
digitalWrite(R2, LOW);
}
void left(){
digitalWrite(L1, HIGH);
digitalWrite(L2, LOW);
digitalWrite(R2, HIGH);
digitalWrite(R1, LOW);
}
void stop(){
digitalWrite(L1, LOW);
digitalWrite(L2, LOW);
digitalWrite(R1, LOW);
digitalWrite(R2, LOW);
}
使用简易红外遥控器玩了一下,感觉明显不如蓝牙的响应灵敏,而且超过三米距离就不太能控制了。不过我觉得这个如果是在家里给小朋友玩玩已经足够了。
此处评论已关闭