常用传感器与执行器¶
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° |
接线¶
代码¶
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);
}
三、舵机(Servo)¶
原理¶
舵机通过 ==PWM 信号的脉冲宽度==控制角度:
脉冲宽度 角度
0.5ms ────── 0°
1.0ms ────── 45°
1.5ms ────── 90°(中位)
2.0ms ────── 135°
2.5ms ────── 180°
PWM 周期: 20ms(50Hz)
接线¶
供电注意
- 小型舵机(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)¶
代码¶
#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)¶
接线¶
接收遥控信号¶
#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 元 |
八、常见问题¶
传感器读数不稳定怎么办?
- 多次采样取平均:连续读 10 次取平均值
- 加滤波算法:移动平均、中值滤波、卡尔曼滤波
- 硬件滤波:信号线加 RC 低通滤波(100nF 电容)
- 稳定供电:传感器 VCC 和 GND 之间加 100nF 去耦电容
- 远离干扰源:布线远离电机、继电器等强电磁干扰源
舵机抖动怎么解决?
- 供电不足:使用独立 5V 电源给舵机供电
- 信号干扰:舵机信号线加 100nF 滤波电容
- 程序问题:避免频繁写入相同角度,到达目标角度后可以
detach()释放
I2C 设备太多引脚不够怎么办?
I2C 是总线协议,所有设备共用 SDA 和 SCL 两根线,不需要额外引脚。只要设备地址不冲突,一条 I2C 总线最多可以挂 127 个设备。地址冲突时使用 TCA9548A 多路复用器。
电机之类的大功率设备怎么用 Arduino 驱动?
Arduino IO 口只能输出最大 40mA 电流,驱动大功率设备需要:
- 小直流电机:L298N / TB6612FNG 驱动板
- 步进电机:ULN2003 / A4988 / DRV8825 驱动板
- 大功率负载(灯带、电磁阀):MOS 管(IRF520 模块)或继电器模块