學習如何用 MCU ADC 讀取類比感測器,及 I²C / SPI 讀取數位感測器。
一、學習目標
- ADC 取樣與校正
- I²C / SPI 介面通訊
- 訊號處理(濾波、線性化)
- 簡單顯示介面(OLED / Serial)
二、實作專案:環境監測站
規格
- 溫度:-10°C ~ 50°C,精度 ±1°C
- 濕度:0–100%,精度 ±5%
- 光度:0–100k lux
- 距離:2–80 cm
- OLED 顯示
- USB 序列輸出
三、感測器選擇
溫度
| 感測器 | 介面 | 範圍 | 精度 | 價格 |
|---|---|---|---|---|
| LM35 | 類比 (10mV/°C) | -55–150°C | ±0.5°C | 低 |
| TMP36 | 類比 | -40–125°C | ±2°C | 低 |
| DS18B20 | 1-Wire | -55–125°C | ±0.5°C | 低 |
| DHT22 | 單線 | -40–80°C, 0–100%RH | ±0.5°C | 低 |
| BME280 | I²C/SPI | -40–85°C, 0–100%, 壓力 | ±1°C | 中 |
| SHT4x | I²C | -40–125°C, 0–100% | ±0.2°C | 中 |
光度
- LDR(光敏電阻)+ 分壓 → ADC
- BH1750(I²C,數位 lux 直接輸出)
- TSL2591(高動態範圍)
距離
- HC-SR04(超音波,2–400cm)
- VL53L0X / VL53L1X(雷射 ToF,I²C)
- Sharp GP2Y0A21(紅外,類比)
四、Arduino 完整程式
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <Adafruit_BME280.h>
#include <BH1750.h>
#define OLED_WIDTH 128
#define OLED_HEIGHT 64
Adafruit_SSD1306 display(OLED_WIDTH, OLED_HEIGHT, &Wire);
Adafruit_BME280 bme;
BH1750 lightMeter;
const int trigPin = 9;
const int echoPin = 10;
void setup() {
Serial.begin(115200);
Wire.begin();
if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
Serial.println("OLED 初始化失敗");
while (1);
}
if (!bme.begin(0x76)) {
Serial.println("BME280 找不到");
while (1);
}
lightMeter.begin();
pinMode(trigPin, OUTPUT);
pinMode(echoPin, INPUT);
}
float readDistance() {
digitalWrite(trigPin, LOW);
delayMicroseconds(2);
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);
long duration = pulseIn(echoPin, HIGH, 30000);
return duration * 0.0343 / 2.0;
}
void loop() {
float t = bme.readTemperature();
float h = bme.readHumidity();
float p = bme.readPressure() / 100.0F;
float lux = lightMeter.readLightLevel();
float dist = readDistance();
Serial.print("T="); Serial.print(t);
Serial.print(" H="); Serial.print(h);
Serial.print(" P="); Serial.print(p);
Serial.print(" Lux="); Serial.print(lux);
Serial.print(" D="); Serial.println(dist);
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(WHITE);
display.setCursor(0, 0);
display.print("Temp: "); display.print(t); display.println(" C");
display.print("Humi: "); display.print(h); display.println(" %");
display.print("Pres: "); display.print(p); display.println(" hPa");
display.print("Lux: "); display.println(lux);
display.print("Dist: "); display.print(dist); display.println(" cm");
display.display();
delay(1000);
}
五、進階主題
1. ADC 校正
實際 ADC 與理想差異: - 偏移誤差 - 增益誤差 - 非線性
校正:
real_value = a * adc_reading + b
用兩點校正法(known low、known high)求 a、b。
2. 類比訊號降雜訊
- 平均濾波:取多次平均
- 中值濾波:去除尖峰
- 指數移動平均(EMA):低通效果
float ema = 0;
const float alpha = 0.1;
ema = alpha * new + (1 - alpha) * ema;
3. I²C 故障排除
- 拉高電阻:4.7 kΩ(標準)、2.2 kΩ(fast mode)
- 地址衝突:用 I²C scanner 確認
- 線太長 → 減少電容、降速
// I²C scanner
for (uint8_t addr = 1; addr < 127; addr++) {
Wire.beginTransmission(addr);
if (Wire.endTransmission() == 0) {
Serial.print("找到 0x");
Serial.println(addr, HEX);
}
}
4. 單位轉換 / 線性化
例如 NTC 熱敏電阻:
$$ \frac{1}{T} = \frac{1}{T_0} + \frac{1}{B} \ln!\left(\frac{R}{R_0}\right) $$
需要查表或多項式擬合。
六、PCB 化(進階)
把麵包板電路畫成 PCB:
- KiCad 畫原理圖
- 元件選 SMD(精緻)
- 整合 USB-C、TypeC、CH340 USB 轉串
- 加入 ESD 保護
- 量產 10 片,分送朋友
七、本專案重點
✅ 您學到:
- [x] ADC 量測類比感測器
- [x] I²C 讀取數位感測器
- [x] OLED 顯示
- [x] 序列通訊 debug
- [x] 訊號濾波
- [x] 多感測器整合