ESP32: akcelerometar i magnetometar (poređenje sa micro:bit-om)
Micro:bit ima ugrađen akcelerometar (meri ubrzanje i nagib) i magnetometar (kompas) – sa njima smo radili u lekcijama senzori: ubrzanje, nagib i pedometar i kompas i temperatura. ESP32 ploča te senzore nema, ali ih lako dodajemo kao male module. U ovoj lekciji povezujemo dva najčešća modula, čitamo ih bez dodatnih biblioteka i poredimo rezultate sa micro:bit-om.
Šta mere ovi senzori
- Akcelerometar meri ubrzanje po tri ose (X, Y, Z) u jedinicama g (1 g ≈ 9,81 m/s², ubrzanje Zemljine teže). Kada senzor miruje, meri samo silu Zemljine teže – zato iz njega možemo da izračunamo nagib. Kada ga protresemo, vrednosti naglo skaču.
- Magnetometar meri magnetno polje po tri ose. Zemljino magnetno polje je slabo (u Srbiji oko 48 µT), ali dovoljno da odredimo smer severa, kao kompas.
Na micro:bit-u (verzija 2) oba senzora su u jednom čipu LSM303AGR. Za ESP32 koristimo dva odvojena, jeftina modula:
| Modul | Čip | Meri | I2C adresa |
|---|---|---|---|
| GY-521 | MPU6050 | ubrzanje (3 ose) i ugaonu brzinu – žiroskop (3 ose) | 0x68 |
| GY-271 | QMC5883L | magnetno polje (3 ose) | 0x0D |
0x1E), a noviji QMC5883L (adresa 0x0D). Izgledaju isto, ali se drugačije programiraju. I2C skener ispod pokazuje koji imaš – program u ovoj lekciji je za QMC5883L.Povezivanje preko I2C magistrale
Oba modula koriste I2C – vezu sa samo dve signalne žice (SDA za podatke i SCL za takt). Na iste dve žice može da se poveže više uređaja, jer svaki ima svoju adresu. Kod ESP32 podrazumevani I2C pinovi su GPIO21 (SDA) i GPIO22 (SCL).
MODUL (GY-521 i GY-271) ESP32
VCC ---------------------> 3V3
GND ---------------------> GND
SDA ---------------------> GPIO21
SCL ---------------------> GPIO22
- Oba modula vezujemo paralelno: SDA na SDA, SCL na SCL.
- Moduli imaju svoj regulator i otpornike za I2C, pa rade na 3,3 V bez dodatnih delova.
- Magnetometar drži bar 10–15 cm dalje od motora, magneta, zvučnika i baterija – oni „kvare“ kompas.
Korak 1: I2C skener
Pre pisanja programa proverimo da li ESP32 „vidi“ module. Skener pita svaku adresu od 1 do 126 da li tu postoji uređaj:
// I2C skener – ispisuje adrese svih uređaja na magistrali
#include <Wire.h>
void setup() {
Serial.begin(115200);
Wire.begin(21, 22); // SDA = GPIO21, SCL = GPIO22
}
void loop() {
Serial.println("Trazim I2C uredjaje...");
for (int adresa = 1; adresa < 127; adresa++) {
Wire.beginTransmission(adresa);
if (Wire.endTransmission() == 0) { // 0 znači: uređaj je odgovorio
Serial.print("Pronadjen uredjaj na adresi 0x");
Serial.println(adresa, HEX);
}
}
delay(3000);
}
Očekivani rezultat: 0x68 (MPU6050) i 0x0D (QMC5883L). Ako ne vidiš ništa – proveri žice SDA i SCL (često su zamenjene) i napajanje.
Korak 2: akcelerometar MPU6050
Senzor ima unutrašnje registre – male memorijske ćelije sa brojevima. Program prvo upiše 0 u registar 0x6B da probudi senzor, a zatim u svakom krugu pročita 6 bajtova počevši od registra 0x3B: po dva bajta za X, Y i Z.
// MPU6050 – čitanje akcelerometra bez dodatne biblioteke
#include <Wire.h>
#include <math.h>
const int MPU = 0x68; // I2C adresa (0x69 ako je pin AD0 na 3,3 V)
int16_t procitaj16() {
int visi = Wire.read(); // prvo stiže viši bajt
int nizi = Wire.read();
return (int16_t)((visi << 8) | nizi);
}
void setup() {
Serial.begin(115200);
Wire.begin(21, 22);
Wire.beginTransmission(MPU);
Wire.write(0x6B); // registar PWR_MGMT_1
Wire.write(0); // 0 = probudi senzor iz režima spavanja
Wire.endTransmission();
}
void loop() {
Wire.beginTransmission(MPU);
Wire.write(0x3B); // prvi registar ubrzanja (ACCEL_XOUT_H)
Wire.endTransmission(false);
Wire.requestFrom(MPU, 6, true); // 6 bajtova: X, Y, Z
int16_t ax = procitaj16();
int16_t ay = procitaj16();
int16_t az = procitaj16();
// podrazumevani opseg je ±2 g: 16384 jedinica = 1 g
float gx = ax / 16384.0;
float gy = ay / 16384.0;
float gz = az / 16384.0;
// nagib (u stepenima) iz pravca sile Zemljine teže
float nagibNapred = atan2(gx, sqrt(gy * gy + gz * gz)) * 180.0 / PI;
float nagibStrana = atan2(gy, gz) * 180.0 / PI;
Serial.print("mg X: "); Serial.print(gx * 1000, 0);
Serial.print(" Y: "); Serial.print(gy * 1000, 0);
Serial.print(" Z: "); Serial.print(gz * 1000, 0);
Serial.print(" nagib napred: "); Serial.print(nagibNapred, 1);
Serial.print(" u stranu: "); Serial.println(nagibStrana, 1);
delay(200);
}
Od sirovog broja do g i stepeni
- Svaka osa je 16-bitni broj od −32768 do 32767. U podrazumevanom opsegu ±2 g, 16384 = 1 g. Zato delimo sa 16384.
- Kada modul leži vodoravno (čipom nagore), očekujemo približno X = 0, Y = 0, Z = 1000 mg.
- Nagib računamo funkcijom
atan2iz toga kako se sila Zemljine teže „raspodelila“ po osama. To važi samo dok se modul ne kreće naglo.
Korak 3: magnetometar QMC5883L (kompas)
Program podesi senzor na neprekidno merenje, a zatim čita tri ose. Smer (azimut) računa se iz X i Y ose: atan2(y, x) daje ugao u radijanima, koji pretvaramo u stepene 0–360.
// QMC5883L – magnetometar (kompas) bez dodatne biblioteke
#include <Wire.h>
#include <math.h>
const int QMC = 0x0D; // I2C adresa QMC5883L
// pomeraji iz kalibracije – na početku 0, kasnije upiši svoje vrednosti
float pomerajX = 0;
float pomerajY = 0;
const float DEKLINACIJA = 5.0; // magnetna deklinacija u Srbiji je oko 5° istočno
void upisi(byte registar, byte vrednost) {
Wire.beginTransmission(QMC);
Wire.write(registar);
Wire.write(vrednost);
Wire.endTransmission();
}
int16_t procitaj16() {
int nizi = Wire.read(); // kod QMC5883L prvo stiže niži bajt
int visi = Wire.read();
return (int16_t)((visi << 8) | nizi);
}
void setup() {
Serial.begin(115200);
Wire.begin(21, 22);
upisi(0x0B, 0x01); // preporučena vrednost za SET/RESET period
upisi(0x09, 0x1D); // neprekidno merenje, 200 Hz, opseg 8 G
}
void loop() {
Wire.beginTransmission(QMC);
Wire.write(0x00); // prvi registar podataka
Wire.endTransmission(false);
Wire.requestFrom(QMC, 6, true);
int16_t x = procitaj16();
int16_t y = procitaj16();
int16_t z = procitaj16();
// smer (azimut) – važi kada modul leži vodoravno
float smer = atan2(y - pomerajY, x - pomerajX) * 180.0 / PI;
smer += DEKLINACIJA;
if (smer < 0) smer += 360;
if (smer >= 360) smer -= 360;
Serial.print("X: "); Serial.print(x);
Serial.print(" Y: "); Serial.print(y);
Serial.print(" Z: "); Serial.print(z);
Serial.print(" smer: "); Serial.println(smer, 0);
delay(250);
}
Kalibracija kompasa
Metalni delovi na ploči i u blizini pomeraju izmerene vrednosti, pa kompas bez kalibracije greši i po nekoliko desetina stepeni. Najjednostavnija kalibracija: okreći modul polako u krug (vodoravno) i pamti najmanju i najveću vrednost X i Y ose. Sredina između njih je pomeraj koji oduzimamo od merenja.
// dodatak za kalibraciju: u loop() posle čitanja x i y
static int16_t minX = 32767, maxX = -32768, minY = 32767, maxY = -32768;
if (x < minX) minX = x;
if (x > maxX) maxX = x;
if (y < minY) minY = y;
if (y > maxY) maxY = y;
Serial.print("pomerajX = "); Serial.print((maxX + minX) / 2.0);
Serial.print(" pomerajY = "); Serial.println((maxY + minY) / 2.0);
Kada se brojevi ustale, upiši ih u pomerajX i pomerajY i ukloni dodatak. Deklinacija je razlika između magnetnog i geografskog severa; u Srbiji je danas oko 5° istočno (za tačnu vrednost svog mesta potraži „magnetic declination“ kalkulator).
compass.heading().Isto merenje na micro:bit-u
Za poređenje, ovo je program u MicroPython-u koji meri iste veličine na micro:bit-u:
# micro:bit (MicroPython) – isto merenje na micro:bit-u
from microbit import *
compass.calibrate() # igrica „popuni ekran“ – nagini micro:bit dok se ne upale sve diode
while True:
x = accelerometer.get_x() # vrednosti su već u mili-g (1000 = 1 g)
y = accelerometer.get_y()
z = accelerometer.get_z()
smer = compass.heading() # 0–359 stepeni, 0 = sever
print(x, y, z, smer) # vidi se u REPL / serijskoj konzoli
sleep(200)
Micro:bit vraća ubrzanje već pretvoreno u mili-g, a za kompas ima ugrađenu kalibraciju (igrica „popuni ekran“). Kod ESP32 te korake pišemo sami – više posla, ali zato tačno vidimo šta senzor radi.
Poređenje: ESP32 + moduli i micro:bit
| ESP32 + MPU6050 + QMC5883L | micro:bit v2 | |
|---|---|---|
| senzori | spoljašnji moduli preko I2C | ugrađeni čip LSM303AGR (akcelerometar + magnetometar) |
| jedinice ubrzanja | sirovi broj, 16384 = 1 g (pretvaramo sami) | mili-g, 1000 ≈ 1 g (oko 1024 na nekim pločama) |
| podrazumevani opseg | ±2 g (može ±4, ±8, ±16 g) | ±2 g (može da se promeni) |
| vodoravno, licem nagore | Z ≈ +1000 mg (čip nagore) | Z ≈ −1000 mg (ekran nagore) |
| žiroskop | da (MPU6050) | ne |
| pokreti (protresi, nagni…) | pišemo sami (npr. velika promena ubrzanja = protresanje) | ugrađeno: accelerometer.was_gesture("shake") |
| kompas | atan2 + ručna kalibracija + deklinacija | compass.calibrate() i compass.heading() |
| jezik | C/C++ (Arduino IDE) | MicroPython ili MakeCode blokovi |
| slanje podataka | Wi-Fi, Bluetooth, serijski port | radio između micro:bit-ova, Bluetooth, serijski port |
Važna razlika su smerovi osa: ista sila Zemljine teže na micro:bit-u daje negativan Z, a na MPU6050 pozitivan, jer su čipovi drugačije okrenuti. Zato pre poređenja uvek proveri: postavi uređaj vodoravno i pogledaj znak Z ose.
Ogled: isto kretanje, dva uređaja
- Pričvrsti ESP32 sa modulima i micro:bit jedan uz drugi (lepljivom trakom na komad kartona), tako da gledaju u istom smeru.
- Postavi ih vodoravno i zapiši X, Y, Z sa oba uređaja. Preračunaj vrednosti ESP32 u mg i uporedi. Koje ose imaju suprotan znak?
- Nagni karton za oko 45° napred. Koliki nagib računa ESP32? Koliki bi bio iz vrednosti micro:bit-a, po istoj formuli?
- Okreni karton u krug i uporedi smer kompasa. Kolika je razlika pre, a kolika posle kalibracije?
- Protresi karton i pogledaj najveće vrednosti. Napiši za ESP32 jednostavno prepoznavanje protresanja: ako je ukupno ubrzanje
sqrt(gx² + gy² + gz²)veće od 2 g – ispiši „protresanje“.
Proveri znanje
Otvori kviz
Kviz: akcelerometar i magnetometar
1. Šta meri akcelerometar kada senzor miruje?
2. Koliko sirovih jedinica MPU6050 odgovara 1 g u opsegu ±2 g?
3. Na kojim pinovima ESP32 je podrazumevana I2C veza?
4. Zašto kompas mora da leži vodoravno (bez kompenzacije nagiba)?
