ESP32: akcelerometar i magnetometar (poređenje sa micro:bit-om)

Lekcija 15 od 17 · Arduino i ESP32

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ČipMeriI2C adresa
GY-521MPU6050ubrzanje (3 ose) i ugaonu brzinu – žiroskop (3 ose)0x68
GY-271QMC5883Lmagnetno polje (3 ose)0x0D
Pažnja kod GY-271: stariji moduli imaju čip HMC5883L (adresa 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 atan2 iz 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).

Kompas mora da leži vodoravno. Ako ga nagneš, deo Zemljinog polja koji „ide u zemlju“ (osa Z) ulazi u X i Y, pa smer postaje pogrešan. Napredni kompasi zato koriste i akcelerometar da izračunaju nagib i isprave ugao (kompenzacija nagiba) – to radi i micro:bit kada pozoveš 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 + QMC5883Lmicro:bit v2
senzorispoljašnji moduli preko I2Cugrađeni čip LSM303AGR (akcelerometar + magnetometar)
jedinice ubrzanjasirovi 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 nagoreZ ≈ +1000 mg (čip nagore)Z ≈ −1000 mg (ekran nagore)
žiroskopda (MPU6050)ne
pokreti (protresi, nagni…)pišemo sami (npr. velika promena ubrzanja = protresanje)ugrađeno: accelerometer.was_gesture("shake")
kompasatan2 + ručna kalibracija + deklinacijacompass.calibrate() i compass.heading()
jezikC/C++ (Arduino IDE)MicroPython ili MakeCode blokovi
slanje podatakaWi-Fi, Bluetooth, serijski portradio 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

  1. Pričvrsti ESP32 sa modulima i micro:bit jedan uz drugi (lepljivom trakom na komad kartona), tako da gledaju u istom smeru.
  2. 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?
  3. Nagni karton za oko 45° napred. Koliki nagib računa ESP32? Koliki bi bio iz vrednosti micro:bit-a, po istoj formuli?
  4. Okreni karton u krug i uporedi smer kompasa. Kolika je razlika pre, a kolika posle kalibracije?
  5. 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)?