Arduino进阶应用
目 录
知识脑图
点击节点可展开/折叠子级,点击🔗图标跳转到对应知识点
知识点字典
按章节分组索引,快速定位所有知识点
Arduino进阶应用
传感器应用
在物联网和创客项目中,传感器是采集外部信息的关键组件。例如,使用温度传感器DS18B20我们可以得到当前的温度,使用霍尔效应传感器可以检测打印机缺纸、车轮和电机的转速等等。
传感器按照接口可以分为以下几类:
- 1. 开关型接口传感器,根据被测对象是否具备某种状态返回开关型电平(通常是高低电平)或具备2种状态(通常是开路和短路)。例如,按键开关就是根据位置呈现短路和开路两种状态;温控开关在温度是否达到温度呈现短路和开路两种状态时。此外,许多Arduino传感器模块(如远红外传感器、土壤湿度传感器)都提供开关型输出。通常可以利用数字IO读入开关型传感器的数据。
- 2. 模拟接口传感器,常常是根据传感对象呈现输出电流、电压或自身阻抗的变换。如光敏/温敏电阻,在光线或温度变化时,其电阻相应发生变化。通常通过分压电路将这些变化转换为电压变化,然后通过ADC读入单片机。在Arduino开发板中,就可以利用模拟输入引脚进行ADC,读入外部的电压数据,从而获得传感器的数据。
- 3. 数字接口传感器,将物理量变换成数字量,单片机需要通过SPI、I2C、单总线等接口读取传感器数据。典型的有18B20、DHT11等等,很多超声波传感器返回脉冲宽度代表距离的电平信号也可以看作数字接口的传感器。Arduino的资源很多,大部分常见的传感器或数字接口都有现成的库,作为初学者可以利用这些库快速完成传感器数据的读取。
下面将依次介绍几种典型的传感器:
开关型接口传感器



- JSM3144单极霍尔开关
其磁电转换特性如下图所示:
使用时按下图连接,就可以用GPIO采集传感器开关数据。
- 热释电红外传感器
热释电红外传感器(PIR)常用于人体检测,当有人靠近时,传感器会接收到人体发出的9~10μm红外线信号。通常与菲涅尔透镜配合使用,以增强检测灵敏度和范围。当检测到信号超过触发阈值时,REL引脚输出高电平,通过GPIO读取REL引脚电平即可实现人体检测。
PIR传感器进行运动检测的例程:
```C
//未验证,引自https://blog.csdn.net/qq_43443531/article/details/86672027
#define pirPin 2
int calibrationTime = 30;
long unsigned int lowIn;
long unsigned int pause = 5000;
boolean lockLow = true;
boolean takeLowTime;
int PIRValue = 0;
void setup() {
Serial.begin(9600);
pinMode(pirPin, INPUT);
}
void loop() {
PIRSensor();
}
void PIRSensor() {
if(digitalRead(pirPin) == HIGH) {
if(lockLow) {
PIRValue = 1;
lockLow = false;
Serial.println("Motion detected.");
delay(50);
}
takeLowTime = true;
}
if(digitalRead(pirPin) == LOW) {
if(takeLowTime){
lowIn = millis();takeLowTime = false;
}
if(!lockLow && millis() - lowIn > pause) {
PIRValue = 0;
lockLow = true;
Serial.println("Motion ended.");
delay(50);
}
}
}
```
模拟接口传感器




- NTC热敏电阻
热敏电阻的电阻值随着温度的变化而改变。通常分为正温度系数热敏电阻——PTC(Positive Temperature Coefficient thermistor)和负温度系数热敏电阻——NTC(Negative Temperature Coefficient thermistor)。手册里其电阻随温度变化的图如下图。这样可以可以利用串联分压的形式,得到一个和温度相关的电压值,利用Arduino的模拟IO读取电压值就可以计算出当前温度。
- 光敏电阻
光敏电阻的工作原理与热敏电阻类似,其阻值随光照强度变化。
- 土壤湿度传感器/水位传感器
土壤湿度传感器利用土壤电阻随湿度变化的原理工作。常用的电路如下图所示,湿度不同时,输出AO的电压也不同。土壤干燥时电阻很大,三极管Q1截止,AO输出0V。
常规ADC
ESP8266裸片ADC输入电压范围为0~1V。但NodeMCU等开发板配有内部分压器,输入范围扩展至0~3.3V。analogRead的值为0~1023。因此如果值为value,则实际电压为:
$$ voltage = \frac{3.3V \times value}{1023}$$
```C
// ESP8266 ADC 测试程序,未验证
#define analogInPin A0 //ESP8266模拟引脚ADC0,也就是A0
int nVoltage = 0;
void setup() {
// 初始化串口的波特率为9600
Serial.begin(9600);
}
void loop() {
//读取模拟数值
nVoltage = analogRead(analogInPin);
// 打印串行监视器中的读数
Serial.print("Voltage = ");
Serial.print((nVoltage*3.3)/1023);
Serial.println(" V");
delay(100);
}
```
ESP32的ADC
ESP32内置2个12位SAR ADC,支持同时采样与转换。
- 1. 改变PWM范围——adcAttachPin()
- 功能:附加一个引脚到ADC(也清除任何其他模拟模式)。
- 使用:
- adcAttachPin(pin);
- 参数:
- pin:连接的引脚。
- 2. 设置ADC位数——analogReadResolution()
- 功能:设置analogRead()返回值的大小(位),默认是十位(返回值介于0到1023)。设置高于硬件实际位数的值无意义;低于实际位数则会丢弃低位。
- 使用:
- analogReadResolution(bits);
- 参数:
- bits:返回值为多少位,返回值的范围是0 ~ $(2^{bits} -1)$。 bits可设置为1~32的整数。
- 相同语句:analogSetWidth(uint8_t bits)
- 3. 读取AD值——analogRead()
- 功能:读取AD值。
- 使用:
- analogWriteRange(pin);
- 参数:
- pin:连接的引脚。
- 返回:ADC的值
- 4. 设置ADC速度——analogSetClockDiv()
- 功能:设置ADC速度。
- 使用:
- analogSetClockDiv(clockDiv);
- 参数:
- clockDiv:分频值,最低为1。
- 返回:无
- 5. 示例程序
```C
// ESP32 ADC测试程序
#define ADC1 34//ADC引脚
void setup(){
Serial.begin(9600);
adcAttachPin(ADC1); //将引脚连接到ADC
}
void loop()
{
analogReadResolution(16); //设置aliogRead返回值的分辨率,超过实际的12位ADC位数
Serial.print("Voltage(16bit):");
Serial.print((analogRead(ADC1)*3.3)/65536); //超过实际位数,计算结果错误
Serial.println(" V");
analogReadResolution(12); //设置aliogRead返回值的分辨率
Serial.print("Voltage(12bit):");
Serial.print((analogRead(ADC1)*3.3)/4096);
Serial.println(" V");
analogReadResolution(10); //设置aliogRead返回值的分辨率
Serial.print("Voltage(10bit):");
Serial.print((analogRead(ADC1)*3.3)/1023);
Serial.println(" V");
delay(500);
}
```
数字接口的传感器




Arduino的方便就在于有很多现成的接口库,可以用来操作很多传感器。例如Adafruit Unified Sensor Driver库,可以操作很多传感器,包括:
- Accelerometers 加速度传感器
- Adafruit_ADXL345:ADXL345是一款小而薄的低功耗3轴加速度计,分辨率高(13位),测量范围达±16g。数字输出数据为16位二进制补码格式,可通过SPI(3线或4线)或I2C数字接口访问。
- Adafruit_LSM303DLHC:具有三维线性加速度传感器和三维数字磁传感器系统
- Adafruit_MMA8451_Library:三轴加速度传感器
- Gyroscope 陀螺仪
- Adafruit_L3GD20_U
- Light
- Adafruit_TSL2561:光-数字转换器
- Adafruit_TSL2591_Library:数字光传感器
- Magnetometers磁传感器
- Adafruit_LSM303DLHC:具有三维线性加速度传感器和三维数字磁传感器系统
- Adafruit_HMC5883_Unified
- Barometric Pressure气压计
- Adafruit_BMP085_Unified
- Adafruit_BMP183_Unified_Library
- Humidity & Temperature 温湿度
- DHT-sensor-library
- Humidity, Temperature, & Barometric Pressure 温湿度和气压
- Adafruit_BME280_Library
- Orientation 惯性测量单元
- Adafruit_BNO055
- All in one device
- Adafruit_LSM9DS0 (accelerometer, gyroscope, magnetometer)
- Adafruit_LSM9DS1 (accelerometer, gyroscope, magnetometer)
因此:使用现成的传感器库和模块化连接方式,是快速上手数字接口传感器的最佳途径。Arduino能够兼容的传感器模块远不止常见的37种,下面仅介绍几个典型的例子。
DHT11温湿度传感器
使用DHT11库可以很方便的完成操作。由于源代码中首行引入的 DHT 库并不是 Arduino IDE 内置的库文件,需要先点击项目 - 加载库 - 管理库进入库管理器,搜索安装如下两个依赖库(Adafruit Unified Sensor 和 DHT sensor library):
示例代码如下:
```C
// ESP8266(NodeMCU AMICA)读取DHT11温度传感器
#include "DHT.h"
#define DHTPIN 5 //使用NodeMCU AMICA GPIO5
#define DHTTYPE DHT11
// Initialize DHT sensor
DHT dht(DHTPIN, DHTTYPE, 15);
void setup() {
// Start Serial
Serial.begin(115200);
// Init DHT
dht.begin();
}
void loop() {
// Reading temperature and humidity
float h = dht.readHumidity();
float t = dht.readTemperature();
// Display data
Serial.print("Humidity: ");
Serial.print(h);
Serial.print(" %\t");
Serial.print("Temperature: ");
Serial.print(t);
Serial.println(" *C ");
// Wait a few seconds between measurements.
delay(2000);
}
```
HCSR04超声波传感器
- 1. 利用库——HCSR04库
```C
//使用HCSR04库操作超声波传感器
#include <HCSR04.h>
// 初始化引脚,第一个参数为Trig引脚,第二个参数为Echo引脚。
UltraSonicDistanceSensor distanceSensor(2, 3);
void setup () {
Serial.begin(9600); // 打开串口监视器
void loop () {
//measureDistanceCm返回超声波测量距离,单位为厘米
Serial.println(distanceSensor.measureDistanceCm());
delay(500);//每个0.5s打印一次
}
```
- 2. 下面是不使用库的方式,引自:https://blog.csdn.net/dg940350951/article/details/92803621
```C
//未验证
//为超声波传感器的Trig和echo引脚创建变量。
//trig引脚连接到数字引脚11,echo引脚连接到数字引脚12。
int trigPin = 11;
int echoPin = 12;
/*
变量duration保存了信号发射和接收之间的时间。
变量cm将以厘米为单位保存距离,
变量inch将以英寸为单位保存距离。
*/
long duration, cm, inches;
//setup()以波特率9600初始化串口,并将trig引脚设置为输出,将echo引脚设置为输入。
void setup() {
Serial.begin (9600);
pinMode(trigPin, OUTPUT);
pinMode(echoPin, INPUT);
}
//loop()通过发送一个10微秒的HIGH脉冲来触发传感器。此前,讲给出一个短的低电平脉冲,确保得到一个干净的高脉冲。
void loop() {
digitalWrite(trigPin, LOW);
delayMicroseconds(20000);
digitalWrite(trigPin, HIGH);
delayMicroseconds(40000);
digitalWrite(trigPin, LOW);
duration = pulseIn(echoPin, HIGH);
/*计算公式:
距离=(传播时间/ 2)x声速
声速为:343m / s = 0.0343cm / uS = 1 / 29.1cm / uS
*/
cm = (duration/2) / 29.1;
inches = (duration/2) / 74;
//打印结果,结果显示在串口监视器
Serial.print(inches);
Serial.print("in, ");
Serial.print(cm);
Serial.print("cm");
Serial.println();
}
```
BMP280-3.3 高精度大气压强传感器模块
可以参考:https://blog.csdn.net/weixin_41659040/article/details/100649062
```C
/*
【Arduino】108种传感器模块系列实验(资料+代码+图形+仿真)
实验二十七:GY-BMP280-3.3 高精度大气压强传感器模块(高度与温度计)
*/
#include <Wire.h>
#include <SPI.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BMP280.h>
#define BMP_SCK 13
#define BMP_MISO 12
#define BMP_MOSI 11
#define BMP_CS 10
Adafruit_BMP280 bmp(BMP_CS, BMP_MOSI, BMP_MISO, BMP_SCK);
void setup() {
Serial.begin(9600);
Serial.println(F("BMP280 测试"));
if (!bmp.begin()) {
Serial.println(F("找不到对应的传感器"));
while (1);
}
}
void loop() {
Serial.print(F("温度 = "));
Serial.print(bmp.readTemperature());
Serial.println(" *C");
Serial.print(F("气压 = "));
Serial.print(bmp.readPressure());
Serial.println(" Pa");
Serial.print(F("海拔 = "));
Serial.print(bmp.readAltitude(1013.25));
Serial.println(" m");
Serial.println();
delay(2000);
}
```
OLED液晶显示
OLED(Organic Light-Emitting Diode)显示屏是嵌入式项目中常用的显示设备,具有自发光、对比度高、功耗低、体积小等优点。最常用的是 0.96 寸 I2C 接口的 SSD1306 OLED 屏。
硬件连接(I2C方式)
| OLED引脚 | 连接到 | 说明 |
|---|---|---|
| VCC | 3.3V | 电源正极 |
| GND | GND | 接地 |
| SCL | D1 (GPIO5) | I2C 时钟线 |
| SDA | D2 (GPIO4) | I2C 数据线 |
安装库
在 Arduino IDE 中,选择"项目"→"加载库"→"管理库",搜索安装以下两个库:
Adafruit SSD1306
Adafruit GFX Library
示例代码
```C
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1
#define SCREEN_ADDRESS 0x3C
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
void setup() {
Serial.begin(9600);
// 初始化OLED,地址0x3C(部分屏幕为0x3D)
if (!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
Serial.println(F("SSD1306 allocation failed"));
for (;;);
}
display.clearDisplay();
display.display();
delay(2000);
}
void loop() {
display.clearDisplay();
// 显示文字
display.setTextSize(1); // 字号1(6x8像素)
display.setTextColor(SSD1306_WHITE);
display.setCursor(0, 0);
display.println(F("Hello, OLED!"));
display.setTextSize(2); // 字号2(12x16像素)
display.setCursor(0, 20);
display.println(F("Arduino"));
display.display();
delay(2000);
}
```
Arduino声音输出
Arduino 可以通过 tone() 函数在指定引脚上产生方波信号,驱动蜂鸣器或小型扬声器发出不同频率的声音。
tone() 函数
| 函数 | 语法 | 说明 |
|---|---|---|
| 播放音调 | tone(pin, frequency) | 在指定引脚输出指定频率的方波 |
| 播放音调(限时) | tone(pin, frequency, duration) | 播放指定时长的音调(毫秒) |
| 停止发声 | noTone(pin) | 停止指定引脚的方波输出 |
常用音符频率
| 音符 | 频率(Hz) | 音符 | 频率(Hz) | 音符 | 频率(Hz) |
|---|---|---|---|---|---|
| 低音Do | 262 | 中音Do | 523 | 高音Do | 1047 |
| 低音Re | 294 | 中音Re | 587 | 高音Re | 1175 |
| 低音Mi | 330 | 中音Mi | 659 | 高音Mi | 1319 |
| 低音Fa | 349 | 中音Fa | 698 | 高音Fa | 1397 |
| 低音Sol | 392 | 中音Sol | 784 | 高音Sol | 1568 |
| 低音La | 440 | 中音La | 880 | 高音La | 1760 |
| 低音Si | 494 | 中音Si | 988 | 高音Si | 1976 |
示例:播放简单旋律
```C
#define BUZZER_PIN D4 // 蜂鸣器连接引脚
// 音符频率定义
#define NOTE_C4 262
#define NOTE_D4 294
#define NOTE_E4 330
#define NOTE_F4 349
#define NOTE_G4 392
#define NOTE_A4 440
#define NOTE_B4 494
#define NOTE_C5 523
// 旋律:小星星
int melody[] = {
NOTE_C4, NOTE_C4, NOTE_G4, NOTE_G4,
NOTE_A4, NOTE_A4, NOTE_G4,
NOTE_F4, NOTE_F4, NOTE_E4, NOTE_E4,
NOTE_D4, NOTE_D4, NOTE_C4
};
int noteDurations[] = {
4, 4, 4, 4, 4, 4, 2,
4, 4, 4, 4, 4, 4, 2
};
void setup() {
pinMode(BUZZER_PIN, OUTPUT);
}
void loop() {
for (int i = 0; i < sizeof(melody) / sizeof(melody[0]); i++) {
int noteDuration = 1000 / noteDurations[i];
tone(BUZZER_PIN, melody[i], noteDuration);
delay(noteDuration * 1.3); // 音符之间加30%间隔
}
delay(2000); // 播放完毕后等待2秒
}
```
调试技巧与常见错误排查
串口打印调试
串口是最基本的调试工具,通过 Serial.print() 输出变量值和程序状态,帮助定位问题:
```C
void setup() {
Serial.begin(9600);
Serial.println("程序启动...");
}
void loop() {
int sensorValue = analogRead(A0);
Serial.print("传感器值 = ");
Serial.println(sensorValue);
delay(1000);
}
```
打开串口监视器:Arduino IDE → 工具 → 串口监视器(快捷键 Ctrl+Shift+M)
常见错误排查表
| 现象 | 可能原因 | 解决方法 |
|---|---|---|
| 编译失败 | 语法错误(缺少分号、括号不匹配) | 检查错误提示行号,逐一修正 |
| 编译失败 | 库未安装 | 项目→加载库→管理库,搜索安装缺失的库 |
| 上传失败 | 开发板型号选择错误 | 工具→开发板,选择正确的板型(如NodeMCU 1.0) |
| 上传失败 | 串口/COM口选择错误 | 工具→端口,选择正确的COM口 |
| 上传失败 | ESP8266需手动进入下载模式 | 按住FLASH按钮不放,点击RST,然后松开FLASH |
| LED不亮 | 接线错误(正负极反接) | 检查LED长脚(正极)接信号端,短脚接GND |
| LED不亮 | 引脚号写错 | 对照引脚图确认使用的GPIO编号 |
| 读数不稳定 | 接触不良 | 检查面包板连接,重新插拔跳线 |
| 读数不稳定 | 缺少去抖处理 | 按键读取加 delay(10) 或使用 millis() 去抖 |
| 串口乱码 | 波特率不匹配 | 确保 Serial.begin() 的参数与串口监视器设置一致 |
| 串口乱码 | ESP8266需设置为115200 | ESP8266默认波特率为115200 |
| 程序运行异常 | 内存不足(SRAM溢出) | 减少全局变量,使用 String 时注意内存碎片 |
| WiFi连接失败 | 路由器2.4G/5G频段问题 | ESP8266仅支持2.4GHz WiFi |