1. Pengenalan Sensor Interfacing
Sensor interfacing adalah proses menghubungkan sensor fisik ke microcontroller sehingga data dari dunia nyata (suhu, cahaya, kelembaban, tekanan, dll) dapat dibaca, diproses, dan dikirim ke sistem IoT. Memahami berbagai metode interfacing adalah keterampilan fundamental bagi setiap praktisi IoT.
Setiap sensor memiliki karakteristik berbeda: ada yang menghasilkan sinyal analog, ada yang digital; ada yang berkomunikasi via I2C, SPI, atau UART. Pemilihan metode interfacing yang tepat mempengaruhi akurasi, kecepatan, dan kompleksitas sistem Anda.
Mengapa Sensor Interfacing Penting?
| Aspek | Penjelasan |
|---|---|
| Akuisisi Data | Sensor adalah mata dan telinga sistem IoT — tanpa sensor, tidak ada data |
| Akurasi | Interfacing yang benar memastikan pembacaan sensor akurat dan konsisten |
| Efisiensi | Pemilihan protokol yang tepat menghemat pin, power, dan bandwidth |
| Skalabilitas | Protokol seperti I2C dan SPI memungkinkan banyak sensor di satu bus |
| Reliability | Kalibrasi dan filtering menghasilkan data yang dapat diandalkan |
Scan Alamat I2C
#include <Wire.h>
// I2C Scanner — cari semua device di bus I2C
// Berguna untuk menemukan alamat sensor baru
#define SDA_PIN 21 // ESP32 default SDA
#define SCL_PIN 22 // ESP32 default SCL
void setup() {
Serial.begin(115200);
Wire.begin(SDA_PIN, SCL_PIN);
Serial.println("I2C Scanner - Scanning...");
Serial.println(" 0 1 2 3 4 5 6 7 8 9 A B C D E F");
int deviceCount = 0;
for (byte addr = 1; addr < 127; addr++) {
if (addr % 16 == 0) {
Serial.printf("\n0x%02X:", addr);
}
Wire.beginTransmission(addr);
byte error = Wire.endTransmission();
if (error == 0) {
Serial.printf(" %02X", addr);
deviceCount++;
} else {
Serial.print(" --");
}
}
Serial.printf("\n\nDitemukan %d perangkat I2C\n", deviceCount);
// Tabel alamat umum
Serial.println("\nAlamat Umum:");
Serial.println(" 0x27/0x3F - LCD I2C");
Serial.println(" 0x3C/0x3D - OLED SSD1306");
Serial.println(" 0x48 - ADS1115 (ADC)");
Serial.println(" 0x68 - MPU6050 (IMU)");
Serial.println(" 0x76/0x77 - BME280/BMP280");
Serial.println(" 0x23 - BH1750 (Light)");
Serial.println(" 0x5A - SGP30 (Gas)");
}
void loop() {}
Membaca BME280 via I2C
#include <Wire.h>
#include <Adafruit_BME280.h>
// I2C Pins untuk ESP32
#define SDA_PIN 21
#define SCL_PIN 22
Adafruit_BME280 bme; // I2C (default address 0x76)
void setup() {
Serial.begin(115200);
Wire.begin(SDA_PIN, SCL_PIN);
// Inisialisasi BME280
if (!bme.begin(0x76)) {
Serial.println("BME280 tidak ditemukan!");
Serial.println("Cek wiring dan alamat I2C (0x76 atau 0x77)");
while (1);
}
// Konfigurasi sensor
bme.setSampling(
Adafruit_BME280::MODE_NORMAL, // Mode operasi
Adafruit_BME280::SAMPLING_X2, // Temperature oversampling
Adafruit_BME280::SAMPLING_X16, // Pressure oversampling
Adafruit_BME280::SAMPLING_X1, // Humidity oversampling
Adafruit_BME280::FILTER_X16, // IIR Filter
Adafruit_BME280::STANDBY_MS_500 // Standby time
);
Serial.println("BME280 siap!");
}
void loop() {
float suhu = bme.readTemperature(); // °C
float tekanan = bme.readPressure() / 100.0; // hPa
float kelembaban = bme.readHumidity(); // %
float altitude = bme.readAltitude(1013.25); // meter (QNH)
Serial.printf("Suhu : %.2f °C\n", suhu);
Serial.printf("Tekanan : %.2f hPa\n", tekanan);
Serial.printf("Kelembaban: %.2f %%\n", kelembaban);
Serial.printf("Altitude : %.2f m\n", altitude);
Serial.println("========================");
delay(2000);
}
5. Komunikasi SPI
SPI (Serial Peripheral Interface) adalah protokol komunikasi serial synchronous yang dikembangkan oleh Motorola. SPI lebih cepat dari I2C dan cocok untuk aplikasi yang membutuhkan throughput tinggi seperti display, ADC berkecepatan tinggi, dan SD card.
Karakteristik SPI
| Parameter | Nilai |
|---|---|
| Jumlah Kabel | 4 (MOSI, MISO, SCK, SS/CS) + VCC + GND |
| Kecepatan | Hingga beberapa MHz (biasanya 1-10 MHz, max ~80 MHz) |
| Topology | Single master, multi-slave (dengan CS per slave) |
| Duplex | Full duplex (bisa kirim dan terima bersamaan) |
| Jarak | Hingga ~30 cm (board-level) |
| Pull-up Resistor | Tidak diperlukan |
SPI Pin dan Fungsi
MCP3008 — ADC Eksternal via SPI
#include <SPI.h>
// MCP3008: 10-bit ADC, 8 channel, SPI interface
// Cocok untuk ESP32 yang ADC-nya non-linear
#define CS_PIN 5 // Chip Select (GPIO 5)
#define SPI_FREQ 1000000 // 1 MHz SPI clock
void setup() {
Serial.begin(115200);
SPI.begin(); // Default: SCK=18, MISO=19, MOSI=23
pinMode(CS_PIN, OUTPUT);
digitalWrite(CS_PIN, HIGH); // Deselect
Serial.println("MCP3008 ADC via SPI siap!");
}
int readMCP3008(int channel) {
// MCP3008 protocol:
// Byte 1: Start bit (1) + Single-ended (1) + Channel (3 bit)
// Byte 2: Don't care (8 clock)
// Byte 3: Don't care (4 clock) + Data (4 bit MSB)
// Byte 4: Data (8 bit LSB) ← tapi kita terima di byte 3 & 4
byte command = 0b11000000 | (channel << 3);
SPI.beginTransaction(SPISettings(SPI_FREQ, MSBFIRST, SPI_MODE0));
digitalWrite(CS_PIN, LOW); // Select MCP3008
SPI.transfer(command); // Kirim command
byte highByte = SPI.transfer(0x00); // Terima 5 bit
byte lowByte = SPI.transfer(0x00); // Terima 8 bit
digitalWrite(CS_PIN, HIGH); // Deselect
SPI.endTransaction();
// Gabungkan 10-bit result
int result = ((highByte & 0x1F) << 8) | lowByte;
return result; // 0 - 1023
}
void loop() {
// Baca semua 8 channel
for (int ch = 0; ch < 8; ch++) {
int raw = readMCP3008(ch);
float voltage = (raw / 1023.0) * 3.3; // Vref = 3.3V
Serial.printf("CH%d: %d (%.3fV) ", ch, raw, voltage);
}
Serial.println();
delay(1000);
}
6. Komunikasi UART/Serial
UART (Universal Asynchronous Receiver-Transmitter) adalah protokol komunikasi serial asynchronous yang paling banyak digunakan. UART tidak memerlukan clock line — komunikasi disinkronisasi berdasarkan baud rate yang disepakati kedua belah pihak.
Karakteristik UART
| Parameter | Nilai |
|---|---|
| Jumlah Kabel | 2 minimum (TX + RX) + GND |
| Baud Rate Umum | 9600, 19200, 38400, 57600, 115200 |
| Topology | Point-to-point (2 device) |
| Duplex | Full duplex (TX dan RX terpisah) |
| Synchronization | Asynchronous (tidak perlu clock) |
| Data Frame | Start bit + Data (5-9 bit) + Parity + Stop bit |
UART Data Frame
UART DATA FRAME
Idle Idle
HIGH DATA BITS
S D0 D1 D2 D3 D4 P S
D5 D6 D7
S = Start bit (selalu LOW, 1 bit)
D0-D7 = Data bits (8 bits, LSB first)
P = Parity bit (opsional: even/odd)
S = Stop bit (selalu HIGH, 1 atau 2 bit)
Contoh mengirim karakter 'A' (0x41 = 01000001):
IdleStart 1 0 0 0 0 0 1 0 StopIdle
HIGH LOW HIGHHIGH
⚠️ TX device → RX device (sambung silang!)
TX1 ► RX2
RX1 ◄ TX2
GPS Module dengan UART
#include <TinyGPS++.h>
#include <HardwareSerial.h>
// GPS Module NEO-6M via UART
// Baud rate default GPS: 9600
#define GPS_RX_PIN 16 // ESP32 RX ← GPS TX
#define GPS_TX_PIN 17 // ESP32 TX → GPS RX (opsional)
TinyGPSPlus gps;
HardwareSerial SerialGPS(2); // UART2 pada ESP32
void setup() {
Serial.begin(115200);
SerialGPS.begin(9600, SERIAL_8N1, GPS_RX_PIN, GPS_TX_PIN);
Serial.println("GPS Module via UART siap!");
Serial.println("Menunggu sinyal GPS...");
}
void loop() {
// Baca data GPS dari UART
while (SerialGPS.available() > 0) {
char c = SerialGPS.read();
gps.encode(c);
}
// Tampilkan data jika ada update
if (gps.location.isUpdated()) {
double lat = gps.location.lat();
double lng = gps.location.lng();
double alt = gps.altitude.meters();
int sats = gps.satellites.value();
Serial.printf("Lat: %.6f, Lng: %.6f\n", lat, lng);
Serial.printf("Altitude: %.1f m, Satellites: %d\n", alt, sats);
// Cek kualitas fix
if (gps.hdop.isValid()) {
Serial.printf("HDOP: %.1f ", gps.hdop.hdop());
if (gps.hdop.hdop() < 2) Serial.println("(Excellent)");
else if (gps.hdop.hdop() < 5) Serial.println("(Good)");
else Serial.println("(Poor)");
}
}
// Tampilkan raw NMEA (debug)
// while (SerialGPS.available()) {
// Serial.write(SerialGPS.read());
// }
}
7. Perbandingan Protokol Komunikasi
Memilih protokol komunikasi yang tepat sangat penting untuk kesuksesan proyek IoT Anda. Berikut perbandingan lengkap antara ADC, I2C, SPI, dan UART.
Tabel Perbandingan Lengkap
| Aspek | Analog/ADC | I2C | SPI | UART |
|---|---|---|---|---|
| Jumlah Kabel | 1 (+ GND) | 2 (+ VCC + GND) | 4 (+ VCC + GND) | 2 (+ GND) |
| Kecepatan | Tergantung ADC | 100K-3.4M Hz | 1-80 MHz | 9600-115200+ baud |
| Duplex | N/A | Half duplex | Full duplex | Full duplex |
| Multi-device | 1 pin/device | 128+ device | Perlu CS/device | Point-to-point |
| Jarak | ~1 meter | ~1 meter | ~30 cm | ~15 meter |
| Pin Usage | 1 per sensor | 2 shared | 4+N CS | 2 per connection |
| Kompleksitas | 🟢 Rendah | 🟡 Sedang | 🟡 Sedang | 🟢 Rendah |
| Cocok untuk | Sensor analog | Banyak sensor | High speed | Module serial |
Kapan Menggunakan Apa?
- Analog/ADC — Sensor sederhana (suhu LM35, cahaya LDR, pH meter) yang outputnya tegangan analog
- I2C — Banyak sensor digital di satu bus (BME280, MPU6050, OLED). Pin-efficient!
- SPI — Butuh kecepatan tinggi (SD card, display TFT, ADC presisi, radio LoRa)
- UART — Module yang sudah punya serial interface (GPS, Bluetooth, PM2.5 sensor)
8. Kalibrasi Sensor
Kalibrasi adalah proses menyesuaikan pembacaan sensor dengan nilai referensi yang diketahui untuk memastikan akurasi. Tanpa kalibrasi, data sensor bisa menyimpang jauh dari nilai sebenarnya.
Metode Kalibrasi
| Metode | Cara Kerja | Cocok Untuk |
|---|---|---|
| Offset | Tambah/kurangi nilai konstan | Drift yang konsisten |
| Linear | y = mx + b (slope dan intercept) | Hubungan linear |
| Polynomial | y = ax² + bx + c | Hubungan non-linear |
| Lookup Table | Tabel mapping input → output | Kurva kompleks |
| Multi-point | Kalibrasi di beberapa titik referensi | Akurasi tinggi |
Kalibrasi pH Meter
// Kalibrasi pH meter dengan 2 titik referensi
// Larutan buffer pH 4.0 dan pH 7.0
class PHSensor {
private:
int _pin;
float _acidVoltage; // Voltage di pH 4.0
float _neutralVoltage; // Voltage di pH 7.0
float _slope;
float _intercept;
public:
PHSensor(int pin) {
_pin = pin;
_acidVoltage = 1.650; // Default, harus dikalibrasi
_neutralVoltage = 1.250; // Default, harus dikalibrasi
_calculateCoefficients();
}
// Kalibrasi dengan 2 titik
void calibrateTwoPoint(float ph1, float voltage1,
float ph2, float voltage2) {
// ph1 = 7.0 (neutral), voltage1 = voltage saat pH 7.0
// ph2 = 4.0 (acid), voltage2 = voltage saat pH 4.0
_neutralVoltage = voltage1;
_acidVoltage = voltage2;
_calculateCoefficients();
Serial.printf("Kalibrasi: slope=%.4f, intercept=%.4f\n",
_slope, _intercept);
}
void _calculateCoefficients() {
// Linear: pH = slope * voltage + intercept
_slope = (7.0 - 4.0) / (_neutralVoltage - _acidVoltage);
_intercept = 7.0 - _slope * _neutralVoltage;
}
float readVoltage() {
long sum = 0;
for (int i = 0; i < 40; i++) {
sum += analogRead(_pin);
delay(10);
}
float avg = sum / 40.0;
return (avg / 4095.0) * 3.3;
}
float readPH() {
float voltage = readVoltage();
float ph = _slope * voltage + _intercept;
return constrain(ph, 0.0, 14.0);
}
};
// Setup kalibrasi
PHSensor phSensor(34);
void setup() {
Serial.begin(115200);
delay(3000);
// Mode kalibrasi
Serial.println("=== KALIBRASI pH METER ===");
Serial.println("Celupkan ke larutan pH 7.0, lalu ketik 'ok'");
while (!Serial.available()) {}
float voltage7 = phSensor.readVoltage();
Serial.printf("Voltage pH 7.0: %.4f V\n", voltage7);
Serial.println("Celupkan ke larutan pH 4.0, lalu ketik 'ok'");
while (!Serial.available()) {}
float voltage4 = phSensor.readVoltage();
Serial.printf("Voltage pH 4.0: %.4f V\n", voltage4);
phSensor.calibrateTwoPoint(7.0, voltage7, 4.0, voltage4);
Serial.println("Kalibrasi selesai!");
}
void loop() {
float ph = phSensor.readPH();
Serial.printf("pH: %.2f\n", ph);
delay(2000);
}
Kalibrasi Sensor Suhu (NTC Thermistor)
// NTC Thermistor dengan Steinhart-Hart equation
// Lebih akurat dari linearisasi sederhana
#include <math.h>
#define THERMISTOR_PIN 34
#define R_FIXED 10000.0 // Resistor tetap 10KΩ
#define R_NTC_25 10000.0 // Resistansi NTC pada 25°C
#define B_COEFFICIENT 3950 // Beta coefficient dari datasheet
float readNTCTemperature() {
// Baca ADC
int raw = analogRead(THERMISTOR_PIN);
// Hitung resistansi NTC
// Voltage divider: Vout = Vcc * R_fixed / (R_ntc + R_fixed)
float voltage = (raw / 4095.0) * 3.3;
float r_ntc = R_FIXED * (3.3 / voltage - 1.0);
// Steinhart-Hart (simplified with B coefficient)
// 1/T = 1/T0 + (1/B) * ln(R/R0)
float tempK = 1.0 / (
(1.0 / 298.15) + // 1/T0 (T0=25°C=298.15K)
(1.0 / B_COEFFICIENT) * log(r_ntc / R_NTC_25)
);
float tempC = tempK - 273.15;
// Atau gunakan persamaan Steinhart-Hart penuh (3 titik kalibrasi):
// 1/T = A + B*ln(R) + C*(ln(R))^3
// Perlu mengukur resistansi pada 3 suhu berbeda
return tempC;
}
void setup() {
Serial.begin(115200);
}
void loop() {
float suhu = readNTCTemperature();
Serial.printf("Suhu: %.2f °C\n", suhu);
delay(1000);
}
9. Noise Filtering dan Signal Processing
Sinyal dari sensor sering terganggu oleh noise (gangguan listrik) dari berbagai sumber seperti motor, switching power supply, dan interferensi RF. Filtering sangat penting untuk mendapatkan data yang bersih dan akurat.
Jenis Filter
| Filter | Kompleksitas | Kelebihan | Kekurangan |
|---|---|---|---|
| Moving Average | 🟢 Mudah | Mudah implementasi, halus | Lambat merespon perubahan cepat |
| Exponential Moving Average | 🟢 Mudah | Responsif, hemat memory | Perlu tuning alpha |
| Median Filter | 🟡 Sedang | Tahan spike/outlier | Kurang halus |
| Kalman Filter | 🔴 Kompleks | Optimal untuk noisy data | Sulit implementasi dan tuning |
| IIR/Butterworth | 🟡 Sedang | Fleksibel (low/high/band pass) | Perlu koefisien filter |
Implementasi Filter
// Implementasi berbagai filter untuk sensor
// 1. Moving Average Filter
class MovingAverage {
private:
float* _buffer;
int _size;
int _index;
float _sum;
bool _filled;
public:
MovingAverage(int size) {
_size = size;
_buffer = new float[size]();
_index = 0;
_sum = 0;
_filled = false;
}
float update(float value) {
_sum -= _buffer[_index];
_buffer[_index] = value;
_sum += value;
_index = (_index + 1) % _size;
if (_index == 0) _filled = true;
return _sum / (_filled ? _size : _index);
}
};
// 2. Exponential Moving Average (EMA)
class EMAFilter {
private:
float _alpha;
float _ema;
bool _initialized;
public:
EMAFilter(float alpha) {
_alpha = constrain(alpha, 0.0, 1.0);
_ema = 0;
_initialized = false;
}
float update(float value) {
if (!_initialized) {
_ema = value;
_initialized = true;
} else {
_ema = _alpha * value + (1.0 - _alpha) * _ema;
}
return _ema;
}
};
// 3. Median Filter
class MedianFilter {
private:
float* _buffer;
int _size;
int _index;
bool _filled;
public:
MedianFilter(int size) {
_size = size;
_buffer = new float[size]();
_index = 0;
_filled = false;
}
float update(float value) {
_buffer[_index] = value;
_index = (_index + 1) % _size;
if (_index == 0) _filled = true;
int count = _filled ? _size : _index;
// Copy and sort
float sorted[count];
for (int i = 0; i < count; i++) sorted[i] = _buffer[i];
for (int i = 0; i < count - 1; i++) {
for (int j = i + 1; j < count; j++) {
if (sorted[i] > sorted[j]) {
float t = sorted[i]; sorted[i] = sorted[j]; sorted[j] = t;
}
}
}
return sorted[count / 2];
}
};
// Penggunaan
MovingAverage filterMA(10); // 10-sample moving average
EMAFilter filterEMA(0.1); // alpha=0.1 (halus)
MedianFilter filterMed(7); // 7-sample median
void loop() {
float raw = analogRead(34);
float filtered_ma = filterMA.update(raw);
float filtered_ema = filterEMA.update(raw);
float filtered_med = filterMed.update(raw);
Serial.printf("Raw: %.0f | MA: %.1f | EMA: %.1f | Med: %.1f\n",
raw, filtered_ma, filtered_ema, filtered_med);
delay(100);
}
10. Proyek Multi-Sensor
Mari gabungkan semua pengetahuan dalam satu proyek: Stasiun Monitoring Lingkungan dengan beberapa sensor yang menggunakan berbagai protokol berbeda.
#include <WiFi.h>
#include <Wire.h>
#include <Adafruit_BME280.h>
#include <BH1750.h>
#include <DHT.h>
// ========== KONFIGURASI PIN ==========
// I2C Bus (shared)
#define SDA_PIN 21
#define SCL_PIN 22
// Analog Sensors
#define MQ135_PIN 34 // Gas sensor (ADC)
#define LM35_PIN 35 // Suhu reference (ADC)
// Digital Sensors
#define DHT_PIN 4 // DHT22 (One-wire digital)
// ========== SENSOR OBJECTS ==========
// I2C sensors
Adafruit_BME280 bme; // BME280 (0x76) - Suhu, Tekanan, Lembab
BH1750 lightMeter(0x23); // BH1750 - Cahaya (lux)
// Digital sensor
DHT dht(DHT_PIN, DHT22); // DHT22 - Backup suhu & lembab
// Analog filter
float filterAnalog(int pin, int samples = 20) {
long sum = 0;
for (int i = 0; i < samples; i++) {
sum += analogRead(pin);
delayMicroseconds(200);
}
return (float)sum / samples;
}
void setup() {
Serial.begin(115200);
// Init I2C bus
Wire.begin(SDA_PIN, SCL_PIN);
// Init BME280
if (!bme.begin(0x76)) {
Serial.println("⚠️ BME280 tidak ditemukan!");
} else {
Serial.println("✅ BME280 OK");
}
// Init BH1750
if (lightMeter.begin(BH1750::CONTINUOUS_HIGH_RES_MODE)) {
Serial.println("✅ BH1750 OK");
} else {
Serial.println("⚠️ BH1750 tidak ditemukan!");
}
// Init DHT22
dht.begin();
Serial.println("✅ DHT22 OK");
// ADC config
analogReadResolution(12);
analogSetAttenuation(ADC_11db);
Serial.println("\n=== Stasiun Monitoring Lingkungan ===\n");
}
void loop() {
// ===== BACA I2C SENSORS =====
float bme_suhu = bme.readTemperature();
float bme_lembab = bme.readHumidity();
float bme_tekanan = bme.readPressure() / 100.0;
float lux = lightMeter.readLightLevel();
// ===== BACA DIGITAL SENSOR =====
float dht_suhu = dht.readTemperature();
float dht_lembab = dht.readHumidity();
// ===== BACA ANALOG SENSORS =====
float mq135_raw = filterAnalog(MQ135_PIN);
float mq135_voltage = (mq135_raw / 4095.0) * 3.3;
float lm35_raw = filterAnalog(LM35_PIN);
float lm35_voltage = (lm35_raw / 4095.0) * 3.3;
float lm35_suhu = lm35_voltage / 0.01;
// ===== KALIBRASI & VALIDASI =====
// Cross-check suhu dari 3 sensor
float suhu_avg = bme_suhu;
int suhu_count = 1;
if (!isnan(dht_suhu)) {
suhu_avg += dht_suhu;
suhu_count++;
}
suhu_avg += lm35_suhu;
suhu_count++;
suhu_avg /= suhu_count;
// ===== TAMPILKAN HASIL =====
Serial.println("╔══════════════════════════════════════╗");
Serial.println("║ STASIUN MONITORING LINGKUNGAN ║");
Serial.println("╠══════════════════════════════════════╣");
Serial.printf("║ Suhu : %.2f °C (rata-rata 3 sensor)\n", suhu_avg);
Serial.printf("║ ├ BME280: %.2f °C\n", bme_suhu);
Serial.printf("║ ├ DHT22 : %.2f °C\n", dht_suhu);
Serial.printf("║ └ LM35 : %.2f °C\n", lm35_suhu);
Serial.printf("║ Kelembaban: %.1f %% (BME280)\n", bme_lembab);
Serial.printf("║ Tekanan : %.1f hPa\n", bme_tekanan);
Serial.printf("║ Cahaya : %.1f lux\n", lux);
Serial.printf("║ Udara (MQ135): %.3f V\n", mq135_voltage);
Serial.println("╚══════════════════════════════════════╝\n");
delay(5000);
}
11. Best Practices
| Aspek | Best Practice | Hindari |
|---|---|---|
| Wiring | Gunakan kabel pendek, twisted pair untuk I2C | Kabel panjang tanpa shielding |
| Power | Decoupling capacitor 100nF dekat sensor | Power langsung dari pin MCU |
| ADC | Multi-sample averaging, kalibrasi | Single read tanpa filtering |
| I2C | Pull-up 4.7KΩ, scan bus saat startup | Asumsi alamat, skip error handling |
| SPI | SPI.beginTransaction() setiap transaksi | SPI tanpa CS management |
| Error Handling | Cek return value, timeout, retry | Assume sensor selalu bekerja |
| Kalibrasi | Kalibrasi berkala, simpan offset di EEPROM | Pakai default tanpa kalibrasi |
12. Quiz: Uji Pemahamanmu!
Setelah membaca tutorial di atas, jawablah 5 pertanyaan berikut untuk menguji pemahamanmu tentang sensor interfacing: