[中文] Arduino + Motor130 + SG90 + IR

Author Avatar
MonsterX 6月1日 发布 | 06月05日最后更新
  • 在其它设备中阅读本文章

由于我的辣鸡 Arduino mini 风扇 项目居然进校“启航杯”决赛了,得去答辩(实际上似乎只有极极极少一部分项目被淘汰了,决赛全校入选了 近两百 465 份作品),不得不再整理一遍所有代码,加上最新的关于解决舵机摇头问题的思路, 爱装 13 的(不,我不是,我没有)我尝试用英语写了这篇文章。说实话,风扇我都快拆的只剩零件了…

在这篇文章中,我将会向你展示如何使用 Arduino 单片机制作一个带有摇头和红外遥控功能的小风扇。这也是我第一次尝试使用英语写东西。我将会尝试使用一些简单的单词来让文章变得易于理解(事实是我不记得复杂的词汇和语法结构。哈哈哈…)
开始项目之前,我们首先需要准备一些材料。这些材料在我之前的博文中已经使用过很多次了,如果你翻看过我以前的博文那么你对它们将不会感到陌生。

  • 一块 Arduino 开发板 ,我们用来控制全部的其他组件。
  • 一个 130 电机 ,我们用它来让风扇转起来。
  • 一个舵机 (SG90) ,用来控制风扇摇头。
  • 一个 J16409 红外传感器 ,用来接收红外信号。
  • 一个 ULN2003 电机驱动模块 ,用来驱动上面说过的 130 电机。Arduino I/O 口产生的电流太小了驱动不了电机,我们需要用这个来放大电流。也许我们也能用三极管来放大…
  • 一个按键开关 ,用来手动控制风扇。
  • 一些杜邦线 .
  • 如果我们需要脱机运行这个风扇(用充电宝来驱动风扇),我们还需要一根额外的 9V 升压线

 

目录
模块 1 :130 电机
模块 2 :舵机
模块 3 :红外遥控
整体

 

 

模块 1 :130 电机

示例

一个基础的例子可能像这样。在这个示例中,我们将电机连接到第 9 号引脚并用这个代码使电机以最大转速转动 1s 然后停止 1s 。

const int MotorPIN = 9;
void setup() {
    pinMode(MotorPIN, OUTPUT);
}
void loop() {
    digitalWrite(MotorPIN, HIGH);
    delay(5000);
    digitalWrite(MotorPIN, LOW);
    delay(5000);
    
    // Use PWM to implement the above code.
    analogWrite(MotorPIN, 255);
    delay(5000);
    analogWrite(MotorPIN, 0);
    delay(5000);
}

重写

为了使我们的风扇有三个档次并可以调整,我们需要改进上述代码。我们将按键开关接入到电路并将开关连接到 Arduino 的第 8 号引脚。这次我们使用 ULN2003 电机驱动模块,将电机的两个线连接到驱动模块的输出端口,并将电机驱动模块的输入端口连接到 Arduino 的第 5 和第 6 号引脚。串联 Arduino 和驱动模块使 Arduino 能够为驱动模块供电。当按钮不断按下时,风扇速度从 0 到 3 循环。最后的代码可能会像这样:

const int motorIn1 = 5;
const int motorIn2 = 6;
const int buttonPin = 8;
const int ledPin = 13;
// Define fan speed
int fanspeed = 0;
#define rank1 150
#define rank2 200
#define rank3 250
// Some variables are used for anti-shake processing of mechanical switches.
int btState;
int lastbtState = LOW;
// The time measured in milisecounds will quickly become a bigger number,
// which can't be stored in an int.
long lastDebounceTime = 0;
long debounceDelay = 50;

void setup() {
    // Set the LED motors as OUTPUT and botton as INPUT.
    pinMode(buttonPin,INPUT);
    pinMode(ledPin,OUTPUT);
    pinMode(motorIn1,OUTPUT);
    pinMode(motorIn2,OUTPUT);
    Serial.begin(9600);
}
 
void loop() {
    // Read the state of the switch.
    int reading = digitalRead(buttonPin);
    // If the button state is different from last time, reset the debouncing timer.
    if (reading != lastbtState) {
        lastDebounceTime = millis();
    }
    // Debounce treatment.
    if ((millis() - lastDebounceTime) > debounceDelay) {
        if (reading != btState) {
            btState = reading;
            if (btState == HIGH) {
                // Toggle the LED if the new button state is HIGH.
                digitalWrite(ledPin,HIGH);
                // Controll the fan speed.
                fanspeed = fanspeed + 1;
                if (fanspeed >= 4) {
                    fanspeed = 0;
                }
            }
            else
                digitalWrite(ledPin,LOW);
        }
    }
    // Save the reading and through the loop next time.
    lastbtState = reading;

    // Fan speed is divided into different grades.
    switch (fanspeed) {
        case 1:
            // When fanspeed=1 set the rotate speed of the motor as rank1=150.
            analogWrite(motorIn1, 0);
            analogWrite(motorIn2, rank1);
            break;
        case 2:
            // When fanspeed=2 set the rotate speed of the motor as rank2=200.
            analogWrite(motorIn1, 0);
            analogWrite(motorIn2, rank2);
            break;
        case 3:
            // When fanspeed=1 set the rotate speed of the motor as rank3=250.
            analogWrite(motorIn1, 0);
            analogWrite(motorIn2, rank3);
            break;
        default:
            // In default situation set the rotate speed of the motor as 0.
            analogWrite(motorIn1, 0);
            analogWrite(motorIn2, 0);
            break;
    }
}

现在我们能通过按键开关控制风扇的开关和转速了,并且当按键按下时 LED 将会点亮。

 

模块 2 :舵机

在使用舵机之前我们需要引入一个额外的库 Servo.h

The Servo library supports up to 12 motors on most Arduino boards and 48 on the Arduino Mega. On boards other than the Mega, use of the library disables analogWrite() (PWM) functionality on pins 9 and 10, whether or not there is a Servo on those pins. On the Mega, up to 12 servos can be used without interfering with PWM functionality; use of 12 to 23 motors will disable PWM on pins 11 and 12.
Arduino - Servo

示例

#include <Servo.h>

Servo myservo;  // create servo object to control a servo
// twelve servo objects can be created on most boards
 
int pos = 0;    // variable to store the servo position
 
void setup() {
    myservo.attach(9);  // attaches the servo on pin 9 to the servo object
}
 
void loop() {
    for (pos = 0; pos <= 180; pos += 1) { // goes from 0 degrees to 180 degrees
        // in steps of 1 degree
        myservo.write(pos);              // tell servo to go to position in variable 'pos'
        delay(15);                       // waits 15ms for the servo to reach the position
    }
    for (pos = 180; pos >= 0; pos -= 1) { // goes from 180 degrees to 0 degrees
        myservo.write(pos);              // tell servo to go to position in variable 'pos'
        delay(15);                       // waits 15ms for the servo to reach the position
    }
}

重写

我们需要舵机拥有两个状态,一个是保持旋转,另一个是保持停止。好的,在电机运行时保持舵机转动。这着实难倒了我。因为我认为电机运行是依靠 loop() 函数循环一遍又一遍地执行 analogWrite(); 语句实现的,而舵机摆动需要持续执行 for() 语句。但是,Arduino 一次只能执行一个语句!所以我试图丢弃 for() 结构。既然 loop() 能不断循环执行,那么...

// Define a variable control servo switch.
int servoSwitch = 0;
 
// Define a variable to control the angle of each step of the servo, and reverse each time it reaches 180º or 0º.
int anglestep = 10;
 
void loop() {
 
    /* ... */
 
    switch (servoSwitch) {
        case 1:
            angle = angle + anglestep;
            myservo.write(angle);
            delay(50);
            if (angle == 180 || angle == 0)
                anglestep = -anglestep;
            break;
        case 0:
            angle = myservo.read();
            break;
    }
}

 

模块 3 :红外遥控

现在我们需要使用红外传感器接收红外信号来控制风扇。 irremote.h 库是必需的。在使用此库之前阅读 官方网站 上的介绍。从 Github 下载此库文件,并将其解压缩在文件夹 .Arduino_ROOT/Library/ 下。

示例

#include <IRremote.h>
 
int RECV_PIN = 11;
IRrecv irrecv(RECV_PIN);
decode_results results;
 
void setup() {
    Serial.begin(9600);
    // In case the interrupt driver crashes on setup, give a clue
    // to the user what's going on.
    Serial.println("Enabling IRin");
    irrecv.enableIRIn(); // Start the receiver
    Serial.println("Enabled IRin");
}
 
void loop() {
    if (irrecv.decode(&results)) {
        Serial.println(results.value, HEX);
        irrecv.resume(); // Receive the next value
    }
    delay(100);
}

重写

现在我们需要实现该功能:在匹配正确的编码之后处理风扇程序中的相应变量。我们使用 21 键遥控器来发送红外信号。不同遥控器产生的红外信号解码后的代码可能不同。您可以 Google 您需要什么的代码或者用示例程序自行尝试。在红外遥控模块中,我的最终代码就像这样:

#include <IRremote.h>
 
const int irReceiverPin = 11;
 
IRrecv irrecv(irReceiverPin);
decode_results results;
 
void setup() {
 
    /* ... */
 
    Serial.begin(9600);
    irrecv.enableIRIn();
}
 
void loop() {
 
    /* ... */
 
    if (irrecv.decode(&results)) {
        /* 
        Serial.print("Detail Information: irCode:");
        Serial.print(results.value, HEX);
        Serial.print(",bits:");
        Serial.println(results.bits);
        */
         
        switch (results.value) {
            case 0xFF22DD:
                Serial.println("Button |<< had been pressed.");
                fanspeed = fanspeed - 1;
                if (fanspeed <= 0) {
                    fanspeed = 0;
                    servoSwitch = 0;
                    angle = myservo.read();
                }
                Serial.println("Finished.");
                break;
            case 0xFF02FD:
                Serial.println("Button >>| has been pressed.");
                fanspeed = fanspeed + 1;
                if (fanspeed >= 3) {
                    fanspeed = 3;
                }
                Serial.println("Finished.");
                break;
            case 0xFF629D:
                Serial.println("Button CH has been pressed.");
                fanspeed = 0;
                servoSwitch = 0;
                angle = myservo.read();
                Serial.println("Finished.");
                break;
            case 0xFF30CF:
                Serial.println("Button 1 has been pressed.");
                fanspeed = 1;
                Serial.println("Finished.");
                break;
            case 0xFF18E7:
                Serial.println("Button 2 has been pressed.");
                fanspeed = 2;
                Serial.println("Finished.");
                break;
            case 0xFF7A85:
                Serial.println("Button 3 has been pressed.");
                fanspeed = 3;
                Serial.println("Finished.");
                break;
            case 0xFFE21D:
                Serial.println("Button CH+ has been pressed.");
                if (fanspeed != 0) {
                    servoSwitch = 1;
                    Serial.println("Start servo. Finished.");
                }
                else
                    Serial.println("Start failed, start fan firstly.");
                break;
            case 0xFFA25D:
                Serial.println("Button CH- has been pressed.");
                servoSwitch = 0;
                angle = myservo.read();
                Serial.println("Finished.");
                break;
        }
         
        delay(600);
        /* Serial.println("Delay is end, try to reset the irReceiver."); */
        while (!irrecv.isIdle());
        irrecv.resume();
        /* Serial.println("Reset Success!"); */
        delay(500);
    }
}

 

整体

最终代码

如果你看懂了上面的代码的功能,那么恭喜,最终的代码将会像这样:

#include <IRremote.h>
#include <Servo.h>
 
const int motorIn1 = 5;
const int motorIn2 = 6;
const int buttonPin = 8;
const int irReceiverPin = 11;
const int ledPin = 13;
 
int fanspeed = 0;
#define rank1 150
#define rank2 200
#define rank3 250
int btState;
int lastbtState = LOW;
long lastDebounceTime = 0;
long debounceDelay = 50;
 
Servo myservo;
int servoSwitch = 0;
int angle = 90;
int anglestep = 1;                           // Here is where you need to customize.
 
IRrecv irrecv(irReceiverPin);
decode_results results;
 
void setup() {
    pinMode(buttonPin,INPUT);
    pinMode(ledPin,OUTPUT);
    pinMode(motorIn1,OUTPUT);
    pinMode(motorIn2,OUTPUT);
    myservo.attach(3);
    Serial.begin(9600);
    irrecv.enableIRIn();
}
 
void loop() {
    // Key switch module code
    int reading = digitalRead(buttonPin);
    if (reading != lastbtState) {
        lastDebounceTime = millis();
    }
    if ((millis() - lastDebounceTime) > debounceDelay) {
        if (reading != btState) {
            btState = reading;
            if (btState == HIGH) {
                digitalWrite(ledPin,HIGH);
                fanspeed = fanspeed + 1;
                if (fanspeed >= 4) {
                    fanspeed = 0;
                    servoSwitch = 0;
                    angle = myservo.read();
                }
            }
            else
                digitalWrite(ledPin,LOW);
        }
    }
    lastbtState = reading;
    
    // Infrared remote control module code
    if (irrecv.decode(&results)) {
        /* 
        Serial.print("Detail Information: irCode:");
        Serial.print(results.value, HEX);
        Serial.print(",bits:");
        Serial.println(results.bits);
        */
        switch (results.value) {
            case 0xFF22DD:
                Serial.println("Button |<< had been pressed.");
                fanspeed = fanspeed - 1;
                if (fanspeed <= 0) {
                    fanspeed = 0;
                    servoSwitch = 0;
                    angle = myservo.read();
                }
                Serial.println("Finished.");
                break;
            case 0xFF02FD:
                Serial.println("Button >>| has been pressed.");
                fanspeed = fanspeed + 1;
                if (fanspeed >= 3) {
                    fanspeed = 3;
                }
                Serial.println("Finished.");
                break;
            case 0xFF629D:
                Serial.println("Button CH has been pressed.");
                fanspeed = 0;
                servoSwitch = 0;
                angle = myservo.read();
                Serial.println("Finished.");
                break;
            case 0xFF30CF:
                Serial.println("Button 1 has been pressed.");
                fanspeed = 1;
                Serial.println("Finished.");
                break;
            case 0xFF18E7:
                Serial.println("Button 2 has been pressed.");
                fanspeed = 2;
                Serial.println("Finished.");
                break;
            case 0xFF7A85:
                Serial.println("Button 3 has been pressed.");
                fanspeed = 3;
                Serial.println("Finished.");
                break;
            case 0xFFE21D:
                Serial.println("Button CH+ has been pressed.");
                if (fanspeed != 0) {
                    servoSwitch = 1;
                    Serial.println("Start servo. Finished.");
                }
                else
                    Serial.println("Start failed, start fan firstly.");
                break;
            case 0xFFA25D:
                Serial.println("Button CH- has been pressed.");
                servoSwitch = 0;
                angle = myservo.read();
                Serial.println("Finished.");
                break;
        }
        delay(600);
        /* Serial.println("Delay is end, try to reset the irReceiver."); */
        while (!irrecv.isIdle());
        irrecv.resume();
        /* Serial.println("Reset Success!"); */
    }
    
    // Fan control
    switch (fanspeed) {
        case 1:
            analogWrite(motorIn1, 0);
            analogWrite(motorIn2, rank1);
            break;
        case 2:
            analogWrite(motorIn1, 0);
            analogWrite(motorIn2, rank2);
            break;
        case 3:
            analogWrite(motorIn1, 0);
            analogWrite(motorIn2, rank3);
            break;
        default:
            analogWrite(motorIn1, 0);
            analogWrite(motorIn2, 0);
            break;
    }
    
    // Servo control
    switch (servoSwitch) {
        case 1:
            myservo.write(angle);
            delay(15);                       // Here is where you need to customize.
            angle = angle + anglestep;
            if (angle == 160 || angle == 20) // Here is where you need to customize.
                anglestep = -anglestep;
            break;
        case 0:
            angle = myservo.read();
            break;
    }
}
 

在这里我仍需要多说几点。为了让你的风扇流畅运转,你需要自定义一些选项。第一个是变量 anglestep (控制每一次循环舵机转动的角度),另一个是由函数 delay(); 产生的延时(转动到一个角度时需要一定时间等待舵机转动到该位置)。我在代码中加入了注释,希望能帮到你。另外一个提醒是:你应该注意是如何将电机连接到舵机上的。不要简单的将他们固定在一起,你需要一些缓冲装置来减小在舵机上的电机震动带来的影响。

成品效果

  占位待编辑

 

本文链接:https://monsterx.cn/Microchips/Arduino-SG90-Motor130-IR-CN.html
本站文章除特殊说明外全部由 MonsterX 原创发布,未经允许禁止以任何形式转载。
如果您发现以上内容含有任何引起不适/侵犯权利/违反法律的内容,请立即 联系站长 进行处理。

选择表情