跳到主要内容

常用库介绍

Arduino 的强大之处在于丰富的库支持。库是预先编写好的代码,让你无需从头实现复杂的功能。本章将介绍 Arduino 开发中最常用的库,包括内置库和常用的第三方库。

库的概念与管理

什么是库

库是一组预定义的函数和类的集合,用于实现特定功能。使用库可以大大简化开发过程,避免重复造轮子。比如,要控制舵机,你不需要自己写 PWM 信号生成代码,只需使用 Servo 库的 write() 方法即可。

库的安装方式

方式一:库管理器安装(推荐)

  1. 打开 Arduino IDE
  2. 点击 项目加载库管理库
  3. 在搜索框中输入库名称
  4. 选择版本并点击安装

方式二:手动安装

  1. 从 GitHub 或其他来源下载库的 ZIP 文件
  2. 点击 项目加载库添加 .ZIP 库
  3. 选择下载的 ZIP 文件

方式三:直接复制

将库文件夹复制到 Arduino 的库目录:

  • Windows: 文档\Arduino\libraries\
  • macOS: ~/Documents/Arduino/libraries/
  • Linux: ~/Arduino/libraries/

库的使用方法

安装库后,需要在代码中包含库的头文件:

#include <库名.h>  // 系统库使用尖括号

// 或
#include "myLibrary.h" // 自定义库使用双引号

包含头文件后,就可以使用库提供的函数和类了。

内置库详解

Arduino IDE 自带了一系列内置库,无需额外安装。

EEPROM 库

EEPROM 是一种断电后数据不会丢失的存储器。Arduino Uno 有 1024 字节的 EEPROM,适合存储配置参数、校准值等小量数据。

基本特性

开发板EEPROM 容量
Uno, Nano1024 字节
Mega 25604096 字节
Leonardo1024 字节
寿命限制

EEPROM 的写入寿命约为 10 万次,请避免频繁写入同一个地址。使用 update() 代替 write() 可以延长寿命。

常用函数

#include <EEPROM.h>

// 读取单个字节
byte value = EEPROM.read(address);

// 写入单个字节(直接写入,即使值相同)
EEPROM.write(address, value);

// 更新单个字节(只在值不同时才写入)
EEPROM.update(address, value);

// 读取任意类型数据
EEPROM.get(address, variable);

// 写入任意类型数据
EEPROM.put(address, variable);

// 获取 EEPROM 大小
unsigned int size = EEPROM.length();

// 使用数组语法访问
EEPROM[0] = 100; // 写入
byte val = EEPROM[0]; // 读取

存储结构体示例

#include <EEPROM.h>

// 定义配置结构体
struct Config {
int deviceId;
float calibration;
char name[20];
bool enabled;
};

const int CONFIG_ADDR = 0;
Config config;

void setup() {
Serial.begin(9600);

// 读取配置
EEPROM.get(CONFIG_ADDR, config);

// 检查是否是有效配置
if (config.deviceId == 0xFFFF) {
// 无效配置,使用默认值
config.deviceId = 1;
config.calibration = 1.0;
strcpy(config.name, "Default");
config.enabled = true;

// 保存默认配置
EEPROM.put(CONFIG_ADDR, config);
Serial.println("已保存默认配置");
}

// 打印当前配置
Serial.print("设备 ID: ");
Serial.println(config.deviceId);
Serial.print("校准值: ");
Serial.println(config.calibration);
Serial.print("名称: ");
Serial.println(config.name);
}

void loop() {}

Servo 库

Servo 库用于控制舵机,支持标准舵机(0-180度)和连续旋转舵机。

基本限制

开发板最大舵机数量
Uno, Nano, Mini12 个
Mega48 个
注意

使用 Servo 库时,D9 和 D10 的 PWM 功能会被禁用(Uno 上)。

常用函数

#include <Servo.h>

Servo myServo;

// 关联引脚
myServo.attach(pin);
myServo.attach(pin, min, max); // 指定脉冲宽度范围

// 设置角度(0-180度)
myServo.write(angle);

// 设置脉冲宽度(微秒)
myServo.writeMicroseconds(us);

// 读取当前角度
int angle = myServo.read();

// 读取当前脉冲宽度
int us = myServo.readMicroseconds();

// 断开连接
myServo.detach();

多舵机控制示例

#include <Servo.h>

// 创建舵机对象数组
Servo servos[4];
const int servoPins[] = {9, 10, 11, 12};

void setup() {
// 初始化所有舵机
for (int i = 0; i < 4; i++) {
servos[i].attach(servoPins[i]);
}

Serial.begin(9600);
Serial.println("多舵机控制系统");
}

void loop() {
// 同时控制多个舵机
for (int angle = 0; angle <= 180; angle += 10) {
for (int i = 0; i < 4; i++) {
servos[i].write(angle);
}
delay(50);
}

// 独立控制
servos[0].write(90); // 舵机 1: 90度
servos[1].write(45); // 舵机 2: 45度
servos[2].write(135); // 舵机 3: 135度
servos[3].write(180); // 舵机 4: 180度
delay(1000);
}

Wire 库(I2C 通信)

Wire 库用于 I2C 总线通信,可以连接各种 I2C 设备,如 OLED 显示屏、温湿度传感器、实时时钟等。

引脚定义

开发板SDASCL
Uno, NanoA4A5
Mega2021
LeonardoD2D3
MKR 系列D11D12

主设备常用函数

#include <Wire.h>

// 初始化为主设备
Wire.begin();

// 开始传输
Wire.beginTransmission(address);

// 写入数据
Wire.write(data);
Wire.write(buffer, length);

// 结束传输
Wire.endTransmission();
Wire.endTransmission(false); // 发送重启条件而非停止

// 请求从设备数据
Wire.requestFrom(address, quantity);

// 检查可用字节数
int available = Wire.available();

// 读取数据
int data = Wire.read();

I2C 设备读写示例

#include <Wire.h>

// BMP280 I2C 地址
const int BMP280_ADDR = 0x76;

void setup() {
Serial.begin(9600);
Wire.begin();

// 读取设备 ID 寄存器
byte id = readRegister(0xD0);
Serial.print("设备 ID: 0x");
Serial.println(id, HEX);
}

void loop() {}

// 读取单个寄存器
byte readRegister(byte reg) {
Wire.beginTransmission(BMP280_ADDR);
Wire.write(reg);
Wire.endTransmission(false);

Wire.requestFrom(BMP280_ADDR, 1);
return Wire.read();
}

// 写入单个寄存器
void writeRegister(byte reg, byte value) {
Wire.beginTransmission(BMP280_ADDR);
Wire.write(reg);
Wire.write(value);
Wire.endTransmission();
}

从设备模式

#include <Wire.h>

const int SLAVE_ADDRESS = 0x08;

void setup() {
Wire.begin(SLAVE_ADDRESS); // 作为从设备
Wire.onReceive(receiveEvent); // 接收事件回调
Wire.onRequest(requestEvent); // 请求事件回调

Serial.begin(9600);
}

void loop() {}

// 接收数据回调
void receiveEvent(int howMany) {
while (Wire.available()) {
char c = Wire.read();
Serial.print(c);
}
}

// 被请求发送数据回调
void requestEvent() {
Wire.write("Hello");
}

SPI 库

SPI 是一种高速同步串行通信协议,常用于 SD 卡、TFT 显示屏、传感器等设备。

引脚定义(Arduino Uno)

信号引脚说明
SSD10从机选择
MOSID11主机输出从机输入
MISOD12主机输入从机输出
SCKD13时钟

常用函数

#include <SPI.h>

// 初始化 SPI
SPI.begin();

// 配置 SPI 参数
SPI.beginTransaction(SPISettings(speed, dataOrder, dataMode));

// 传输数据
byte received = SPI.transfer(data);
SPI.transfer(buffer, length);

// 结束传输
SPI.endTransaction();

// 禁用 SPI
SPI.end();

SPI 设备通信示例

#include <SPI.h>

const int SS_PIN = 10;

void setup() {
Serial.begin(9600);

pinMode(SS_PIN, OUTPUT);
digitalWrite(SS_PIN, HIGH);

SPI.begin();
}

void loop() {
// 读取数据
byte data = readSPI(0x00);
Serial.println(data);
delay(1000);
}

byte readSPI(byte address) {
digitalWrite(SS_PIN, LOW);

SPI.beginTransaction(SPISettings(1000000, MSBFIRST, SPI_MODE0));
SPI.transfer(address | 0x80); // 读命令
byte data = SPI.transfer(0x00);
SPI.endTransaction();

digitalWrite(SS_PIN, HIGH);
return data;
}

SoftwareSerial 库

SoftwareSerial 库可以在任意数字引脚上模拟串口通信,当硬件串口不够用时非常有用。

限制与注意事项

  • 不能同时接收和发送数据
  • 最高波特率约 115200
  • 某些引脚可能不支持(仅部分引脚支持引脚变化中断)
  • 会占用更多 CPU 资源

常用函数

#include <SoftwareSerial.h>

// 创建软串口,RX, TX
SoftwareSerial mySerial(10, 11);

// 初始化
mySerial.begin(baudrate);

// 发送数据
mySerial.print(data);
mySerial.println(data);
mySerial.write(data);

// 接收数据
if (mySerial.available()) {
char c = mySerial.read();
}

// 设置监听(多个软串口时)
mySerial.listen();

多串口通信示例

#include <SoftwareSerial.h>

// 硬件串口连接电脑
// 软串口连接 ESP8266
SoftwareSerial espSerial(10, 11);

void setup() {
Serial.begin(9600); // 电脑
espSerial.begin(115200); // ESP8266

Serial.println("双串口桥接器");
}

void loop() {
// 从电脑转发到 ESP8266
if (Serial.available()) {
espSerial.write(Serial.read());
}

// 从 ESP8266 转发到电脑
if (espSerial.available()) {
Serial.write(espSerial.read());
}
}

常用第三方库

ArduinoJson

ArduinoJson 是处理 JSON 数据的标准库,广泛用于物联网项目。

安装

在库管理器中搜索 "ArduinoJson" 并安装。

基本用法

#include <ArduinoJson.h>

void setup() {
Serial.begin(9600);

// 创建 JSON 文档
StaticJsonDocument<256> doc;

// 添加数据
doc["sensor"] = "DHT22";
doc["temperature"] = 23.5;
doc["humidity"] = 65.0;
doc["timestamp"] = 1234567890;

// 序列化为字符串
String output;
serializeJson(doc, output);
Serial.println(output);

// 解析 JSON 字符串
const char* json = "{\"name\":\"Arduino\",\"version\":1}";
StaticJsonDocument<128> doc2;
deserializeJson(doc2, json);

const char* name = doc2["name"];
int version = doc2["version"];

Serial.print("Name: ");
Serial.println(name);
Serial.print("Version: ");
Serial.println(version);
}

void loop() {}

Adafruit_Sensor + DHT

这是读取 DHT 系列温湿度传感器的常用库组合。

安装

  1. 安装 "Adafruit Unified Sensor"
  2. 安装 "DHT sensor library"

使用示例

#include <DHT.h>

#define DHT_PIN 2
#define DHT_TYPE DHT22

DHT dht(DHT_PIN, DHT_TYPE);

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

void loop() {
delay(2000); // DHT22 需要至少 2 秒间隔

float humidity = dht.readHumidity();
float temperature = dht.readTemperature();

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

// 计算体感温度
float heatIndex = dht.computeHeatIndex(temperature, humidity, false);

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

LiquidCrystal_I2C

简化 I2C 接口 LCD1602 显示屏的使用。

安装

在库管理器中搜索 "LiquidCrystal I2C"。

使用示例

#include <LiquidCrystal_I2C.h>

// 地址通常是 0x27 或 0x3F
LiquidCrystal_I2C lcd(0x27, 16, 2);

void setup() {
lcd.init();
lcd.backlight();

lcd.setCursor(0, 0);
lcd.print("Hello, World!");

lcd.setCursor(0, 1);
lcd.print("Arduino LCD");
}

void loop() {
// 闪烁背光
lcd.noBacklight();
delay(500);
lcd.backlight();
delay(500);
}

FastLED

控制 WS2812 等可编程 LED 灯带的强大库。

安装

在库管理器中搜索 "FastLED"。

使用示例

#include <FastLED.h>

#define LED_PIN 6
#define NUM_LEDS 30
#define BRIGHTNESS 50
#define LED_TYPE WS2812
#define COLOR_ORDER GRB

CRGB leds[NUM_LEDS];

void setup() {
FastLED.addLeds<LED_TYPE, LED_PIN, COLOR_ORDER>(leds, NUM_LEDS);
FastLED.setBrightness(BRIGHTNESS);
}

void loop() {
// 彩虹效果
static uint8_t hue = 0;
fill_rainbow(leds, NUM_LEDS, hue, 7);
FastLED.show();
hue++;
delay(10);
}

Adafruit_SSD1306

控制 SSD1306 驱动的 OLED 显示屏。

安装

  1. 安装 "Adafruit GFX Library"
  2. 安装 "Adafruit SSD1306"

使用示例

#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)) {
for (;;); // 初始化失败,停止
}

display.clearDisplay();

display.setTextSize(2);
display.setTextColor(SSD1306_WHITE);
display.setCursor(10, 25);
display.println("OLED Test");

display.display();
}

void loop() {}

创建自定义库

当你的代码需要在多个项目中复用时,可以创建自己的库。

库的基本结构

MyLibrary/
├── MyLibrary.h // 头文件
├── MyLibrary.cpp // 实现文件
├── keywords.txt // 关键字高亮(可选)
└── README.md // 说明文档(可选)

头文件示例

// MyLibrary.h
#ifndef MYLIBRARY_H
#define MYLIBRARY_H

#include <Arduino.h>

class MyLED {
private:
int pin;
bool state;

public:
MyLED(int pin);
void begin();
void on();
void off();
void toggle();
bool getState();
};

#endif

实现文件示例

// MyLibrary.cpp
#include "MyLibrary.h"

MyLED::MyLED(int pin) {
this->pin = pin;
this->state = false;
}

void MyLED::begin() {
pinMode(pin, OUTPUT);
digitalWrite(pin, LOW);
}

void MyLED::on() {
digitalWrite(pin, HIGH);
state = true;
}

void MyLED::off() {
digitalWrite(pin, LOW);
state = false;
}

void MyLED::toggle() {
state ? off() : on();
}

bool MyLED::getState() {
return state;
}

使用自定义库

#include <MyLibrary.h>

MyLED led(13);

void setup() {
led.begin();
}

void loop() {
led.toggle();
delay(1000);
}

库的最佳实践

选择库的建议

  1. 优先选择官方库:官方库通常更稳定、维护更好
  2. 查看更新日期:长时间未更新的库可能有问题
  3. 阅读文档:好的库应该有清晰的使用文档
  4. 检查示例代码:示例代码可以帮助快速上手
  5. 关注社区反馈:查看 GitHub stars 和 issues

使用库的注意事项

  1. 内存占用:大型库可能占用大量内存,注意监控
  2. 引脚冲突:不同库可能使用相同的引脚或定时器
  3. 兼容性:某些库可能不支持所有 Arduino 开发板
  4. 版本问题:更新库时注意 API 是否有变化

库冲突排查

当多个库冲突时,检查以下方面:

  • 是否使用了相同的引脚
  • 是否使用了相同的定时器
  • 是否有内存不足的问题
  • 是否有命名冲突

常见问题

库安装后找不到

  • 确认安装路径正确
  • 重启 Arduino IDE
  • 检查库文件夹名称是否正确

编译错误:未定义的引用

  • 确认正确包含了头文件
  • 检查库名称拼写是否正确
  • 确认库已正确安装

程序太大或内存不足

  • 移除未使用的库
  • 使用更轻量的替代库
  • 优化代码减少内存使用

下一步

掌握常用库后,你已经具备了开发复杂 Arduino 项目的能力。接下来可以通过实战项目来综合运用所学知识。