跳转至

常用传感器与执行器

Arduino 的魅力在于能轻松连接各种传感器和执行器。本节介绍最常用的外设模块,包括接线方式、驱动代码和实用技巧。


一、温湿度传感器(DHT11 / DHT22)

对比

参数 DHT11 DHT22 (AM2302)
温度范围 0~50°C -40~80°C
温度精度 ±2°C ±0.5°C
湿度范围 20~90% RH 0~100% RH
湿度精度 ±5% ±2%
采样间隔 1 秒 2 秒
价格 ~3 元 ~8 元

接线

  Arduino          DHT11/22
    5V  ──────── VCC (Pin 1)
    D2  ──┬───── DATA (Pin 2)
        [4.7KΩ]  ← 上拉电阻
    5V  ──┘
    GND ──────── GND (Pin 4)

    (Pin 3 悬空)

代码

#include <DHT.h>

#define DHT_PIN 2
#define DHT_TYPE DHT11    // 或 DHT22

DHT dht(DHT_PIN, DHT_TYPE);

void setup() {
    Serial.begin(115200);
    dht.begin();
}

void loop() {
    delay(2000);  // DHT 采样间隔至少 1~2 秒

    float humidity = dht.readHumidity();
    float temperature = dht.readTemperature();      // 摄氏度
    float tempF = dht.readTemperature(true);         // 华氏度

    if (isnan(humidity) || isnan(temperature)) {
        Serial.println("读取 DHT 失败!");
        return;
    }

    // 体感温度(热指数)
    float heatIndex = dht.computeHeatIndex(temperature, humidity, false);

    Serial.print("湿度: ");
    Serial.print(humidity);
    Serial.print("% | 温度: ");
    Serial.print(temperature);
    Serial.print("°C | 体感: ");
    Serial.print(heatIndex);
    Serial.println("°C");
}

安装 DHT 库

Arduino IDE → 库管理器 → 搜索 "DHT sensor library"(by Adafruit)→ 安装。同时需要安装依赖库 "Adafruit Unified Sensor"。


二、超声波测距(HC-SR04)

原理

HC-SR04 通过发射 40kHz 超声波脉冲,测量回波时间来计算距离:

\[d = \frac{v \times t}{2} = \frac{340 \text{ m/s} \times t}{2}\]
参数
工作电压 5V
测量范围 2~400 cm
精度 ±3 mm
测量角度 15°

接线

  Arduino          HC-SR04
    5V  ──────── VCC
    D9  ──────── Trig (触发)
    D10 ──────── Echo (回波)
    GND ──────── GND

代码

const int TRIG_PIN = 9;
const int ECHO_PIN = 10;

void setup() {
    Serial.begin(115200);
    pinMode(TRIG_PIN, OUTPUT);
    pinMode(ECHO_PIN, INPUT);
}

void loop() {
    float distance = measureDistance();

    Serial.print("距离: ");
    Serial.print(distance, 1);
    Serial.println(" cm");

    delay(100);
}

float measureDistance() {
    // 发送 10μs 高电平脉冲触发测量
    digitalWrite(TRIG_PIN, LOW);
    delayMicroseconds(2);
    digitalWrite(TRIG_PIN, HIGH);
    delayMicroseconds(10);
    digitalWrite(TRIG_PIN, LOW);

    // 测量回波脉冲宽度(微秒)
    long duration = pulseIn(ECHO_PIN, HIGH, 30000);  // 超时 30ms

    if (duration == 0) return -1;  // 超时(超出测量范围)

    // 距离 = 时间 × 声速 / 2
    // 声速约 340m/s = 0.034 cm/μs
    return duration * 0.034 / 2.0;
}

NewPing 库(更高效)

#include <NewPing.h>

NewPing sonar(9, 10, 400);  // Trig, Echo, 最大距离 cm

void loop() {
    unsigned int distance = sonar.ping_cm();
    Serial.print("距离: ");
    Serial.print(distance);
    Serial.println(" cm");
    delay(50);
}

多次测量取中值

超声波容易受干扰,建议多次测量取中值:

unsigned int distance = sonar.ping_median(5);  // 5 次取中值


三、舵机(Servo)

原理

舵机通过 ==PWM 信号的脉冲宽度==控制角度:

脉冲宽度        角度
0.5ms  ──────  0°
1.0ms  ──────  45°
1.5ms  ──────  90°(中位)
2.0ms  ──────  135°
2.5ms  ──────  180°

PWM 周期: 20ms(50Hz)

接线

  Arduino          SG90 舵机
    5V  ──────── 红线(VCC)
    D9  ──────── 橙线(信号)
    GND ──────── 棕线(GND)

供电注意

  • 小型舵机(SG90)可直接用 Arduino 5V 供电
  • 多个舵机或大扭矩舵机(MG996R)必须外部供电,否则电流不足会导致 Arduino 复位
  • 外部供电时,电源 GND 要和 Arduino GND 相连

代码

#include <Servo.h>

Servo myServo;

void setup() {
    myServo.attach(9);      // 舵机信号线接 D9
    // myServo.attach(9, 500, 2500);  // 自定义脉宽范围
}

void loop() {
    // 方式 1:直接设置角度
    myServo.write(0);       // 转到 0°
    delay(1000);
    myServo.write(90);      // 转到 90°
    delay(1000);
    myServo.write(180);     // 转到 180°
    delay(1000);
}

电位器控制舵机

#include <Servo.h>

Servo myServo;

void setup() {
    myServo.attach(9);
}

void loop() {
    int potVal = analogRead(A0);                // 0~1023
    int angle = map(potVal, 0, 1023, 0, 180);   // 映射到 0~180°
    myServo.write(angle);
    delay(15);  // 给舵机响应时间
}

舵机缓慢移动

void slowMove(Servo &servo, int targetAngle, int delayMs) {
    int currentAngle = servo.read();
    int step = (targetAngle > currentAngle) ? 1 : -1;

    for (int a = currentAngle; a != targetAngle; a += step) {
        servo.write(a);
        delay(delayMs);  // 每步延时,越大越慢
    }
}

// 使用
slowMove(myServo, 180, 15);  // 缓慢转到 180°

四、OLED 显示屏(SSD1306)

常见规格

参数 0.96 寸 1.3 寸
分辨率 128×64 128×64
接口 I2C / SPI I2C / SPI
地址 0x3C / 0x3D 0x3C
驱动芯片 SSD1306 SH1106

接线(I2C)

  Arduino          OLED
    5V  ──────── VCC
    GND ──────── GND
    A4  ──────── SDA
    A5  ──────── SCL

代码

#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

void setup() {
    if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
        Serial.println("SSD1306 初始化失败!");
        for (;;);  // 停止
    }

    display.clearDisplay();
    display.setTextSize(1);          // 字体大小 1(6×8 像素)
    display.setTextColor(SSD1306_WHITE);
    display.setCursor(0, 0);
    display.println("Hello Arduino!");
    display.setTextSize(2);
    display.println("OLED OK");
    display.display();               // 刷新显示
}

void loop() {
    // 显示传感器数据
    float temp = 25.6;
    float hum = 65.3;

    display.clearDisplay();
    display.setTextSize(1);
    display.setCursor(0, 0);
    display.println("=== Weather ===");
    display.setTextSize(2);
    display.setCursor(0, 16);
    display.print(temp, 1);
    display.println(" C");
    display.print(hum, 1);
    display.println(" %");
    display.display();

    delay(1000);
}

常用绘图函数

函数 功能
display.drawPixel(x, y, color) 画点
display.drawLine(x0, y0, x1, y1, color) 画线
display.drawRect(x, y, w, h, color) 画矩形
display.fillRect(x, y, w, h, color) 填充矩形
display.drawCircle(x, y, r, color) 画圆
display.drawBitmap(x, y, bitmap, w, h, color) 画位图
display.setTextSize(n) 设置字号(1~4)
display.setCursor(x, y) 设置光标位置

安装库

需要安装两个库:

  • Adafruit SSD1306:驱动库
  • Adafruit GFX Library:图形库(自动安装为依赖)

五、步进电机(28BYJ-48 + ULN2003)

参数

参数
工作电压 5V
步距角 5.625° / 64(减速比 1:64)
一圈步数 2048 步(半步模式)/ 4096 步
最大频率 ~500 PPS

接线

  Arduino          ULN2003 驱动板
    D8  ──────── IN1
    D9  ──────── IN2
    D10 ──────── IN3
    D11 ──────── IN4
    5V  ──────── VCC (外部供电更好)
    GND ──────── GND

代码(Stepper 库)

#include <Stepper.h>

const int STEPS_PER_REV = 2048;  // 28BYJ-48 一圈 2048 步
Stepper stepper(STEPS_PER_REV, 8, 10, 9, 11);  // 注意引脚顺序!

void setup() {
    stepper.setSpeed(10);  // 10 RPM
    Serial.begin(115200);
}

void loop() {
    Serial.println("顺时针一圈");
    stepper.step(STEPS_PER_REV);     // 正转一圈
    delay(500);

    Serial.println("逆时针一圈");
    stepper.step(-STEPS_PER_REV);    // 反转一圈
    delay(500);
}

引脚顺序

Stepper 库的引脚顺序是 IN1, IN3, IN2, IN4(不是 1234),这是因为步进电机内部线圈的激励顺序。接错顺序会导致电机==只振动不转==。


六、红外遥控(VS1838B + IRremote)

接线

  Arduino          VS1838B
    5V  ──────── VCC (左脚)
    GND ──────── GND (中脚)
    D11 ──────── OUT (右脚)

接收遥控信号

#include <IRremote.hpp>

const int IR_PIN = 11;

void setup() {
    Serial.begin(115200);
    IrReceiver.begin(IR_PIN, ENABLE_LED_FEEDBACK);
    Serial.println("红外接收就绪");
}

void loop() {
    if (IrReceiver.decode()) {
        Serial.print("协议: ");
        Serial.print(getProtocolString(IrReceiver.decodedIRData.protocol));
        Serial.print("  编码: 0x");
        Serial.print(IrReceiver.decodedIRData.decodedRawData, HEX);
        Serial.print("  命令: 0x");
        Serial.println(IrReceiver.decodedIRData.command, HEX);

        // 根据按键执行动作
        switch (IrReceiver.decodedIRData.command) {
            case 0x45: Serial.println("→ 按下: CH-"); break;
            case 0x46: Serial.println("→ 按下: CH");  break;
            case 0x47: Serial.println("→ 按下: CH+"); break;
            case 0x44: Serial.println("→ 按下: |<<"); break;
            case 0x40: Serial.println("→ 按下: >>|"); break;
            case 0x43: Serial.println("→ 按下: >||"); break;
        }

        IrReceiver.resume();  // 准备接收下一个信号
    }
}

获取遥控器编码

先运行上面的代码,逐个按下遥控器的按键,记录每个按键对应的 command 值,然后在 switch 中使用。


七、传感器选型参考

需求 推荐传感器 接口 参考价格
温湿度 DHT22 / BME280 单线 / I2C 8~15 元
距离测量 HC-SR04(超声波) GPIO 3 元
红外距离 VL53L0X(ToF) I2C 15 元
气压/海拔 BMP280 / BME280 I2C 5~15 元
加速度/陀螺仪 MPU6050 / MPU9250 I2C 5~20 元
光照强度 BH1750 I2C 3 元
颜色识别 TCS34725 I2C 10 元
土壤湿度 电容式土壤传感器 ADC 5 元
烟雾/气体 MQ-2 / MQ-135 ADC 5 元
声音检测 MAX9814 麦克风 ADC 8 元
人体感应 HC-SR501(PIR) GPIO 3 元
GPS 定位 NEO-6M / NEO-M8N UART 15~30 元

八、常见问题

传感器读数不稳定怎么办?

  1. 多次采样取平均:连续读 10 次取平均值
  2. 加滤波算法:移动平均、中值滤波、卡尔曼滤波
  3. 硬件滤波:信号线加 RC 低通滤波(100nF 电容)
  4. 稳定供电:传感器 VCC 和 GND 之间加 100nF 去耦电容
  5. 远离干扰源:布线远离电机、继电器等强电磁干扰源

舵机抖动怎么解决?

  • 供电不足:使用独立 5V 电源给舵机供电
  • 信号干扰:舵机信号线加 100nF 滤波电容
  • 程序问题:避免频繁写入相同角度,到达目标角度后可以 detach() 释放

I2C 设备太多引脚不够怎么办?

I2C 是总线协议,所有设备共用 SDA 和 SCL 两根线,不需要额外引脚。只要设备地址不冲突,一条 I2C 总线最多可以挂 127 个设备。地址冲突时使用 TCA9548A 多路复用器。

电机之类的大功率设备怎么用 Arduino 驱动?

Arduino IO 口只能输出最大 40mA 电流,驱动大功率设备需要:

  • 小直流电机:L298N / TB6612FNG 驱动板
  • 步进电机:ULN2003 / A4988 / DRV8825 驱动板
  • 大功率负载(灯带、电磁阀):MOS 管(IRF520 模块)或继电器模块