Skip to content

Kontekst AI — Perun BLE Protocol

Plik przeznaczony dla AI pomagającego pisać aplikację kliencką komunikującą się z urządzeniami MWT.

Zawiera pełny opis protokołu BLE: serwis Perun, ramki danych (Data Frame) dla wszystkich modułów, protokół Control (SET/GET), klucze, kalibracje i kody błędów. Firmware dostarcza producent — ten dokument opisuje wyłącznie interfejs po stronie klienta (Android, iOS, Web, PC).

Pobierz plik .md :material-download:


1. BLE — Serwis Perun

Urządzenia MWT eksponują serwis Perun z czterema charakterystykami:

Charakterystyka UUID Właściwości
Service 457bbb14-9c79-44a8-9810-f17bd358a200
Data 457bbb14-9c79-44a8-9810-f17bd358a201 Notify
Control 457bbb14-9c79-44a8-9810-f17bd358a202 Write + Indicate
Device Info 457bbb14-9c79-44a8-9810-f17bd358a203 Read
RTCM Stream 457bbb14-9c79-44a8-9810-f17bd358a204 Write Without Response

Dodatkowo dostępny jest standardowy Battery Service (BAS):

Charakterystyka UUID Właściwości
BAS Service 0000180f-0000-1000-8000-00805f9b34fb
Battery Level 00002a19-0000-1000-8000-00805f9b34fb Read + Notify

Varianty urządzeń (z Device Info):

Wartość Wariant Typowe moduły
0x01 MWT Base Station GNSS, Barometer (×4), Battery
0x02 MWT Body Module AccGyro, Barometer, Battery, GNSS, Magnetometer, Quaternion
0x03 MWT ETU AccGyro, Battery, Selector, Trigger, Kickback
0x04 MWT Head Module AccGyro, Barometer, Battery, Magnetometer, Quaternion

2. Device Info (Charakterystyka a203 — READ)

Po połączeniu odczytaj tę charakterystykę. Payload w formacie TLV (Type-Length-Value):

[type (1B)][length (1B)][value (length B)] ...
Type Nazwa Opis
0x01 MODULES_AVAILABLE Tablica 1B ID dostępnych modułów
0x02 VARIANT_ID uint8_t — wariant urządzenia
0x03 FW_VERSION 3B: [major][minor][patch]

Przykład — MWT Body Module, firmware 2.1.0:

01 06 02 03 04 06 09 0A   ← MODULES_AVAILABLE: AccGyro(2),Baro(3),Bat(4),Mag(6),Quat(9),UI(10)
02 01 02                   ← VARIANT_ID: Body Module
03 03 02 01 00             ← FW_VERSION: 2.1.0


3. Identyfikatory modułów

ID Stała Moduł Sensor
0x01 MODULE_SYSTEM System
0x02 MODULE_ACC_GYRO Akcelerometr + Żyroskop LSM6DSV32X
0x03 MODULE_BAROMETER Barometr BMP581
0x04 MODULE_BATTERY Napięcie baterii ADC
0x05 MODULE_GNSS GNSS / RTK ZED-F9P
0x06 MODULE_MAGNETOMETER Magnetometr MMC5983MA
0x07 MODULE_SELECTOR Selektor trybu ognia TMAG5273C1
0x08 MODULE_TRIGGER Trigger TMAG5273C1
0x09 MODULE_QUATERNION Fuzja orientacji (software)
0x0A MODULE_PLAYER_STATE Stan gracza
0x0B MODULE_UI UI
0x0C MODULE_KICKBACK Kickback
0x0D MODULE_DISPLACEMENT Estymacja przemieszczenia (software)

4. Data Frame — Ramka danych (Charakterystyka a201 — NOTIFY)

Urządzenie wysyła ramki notify gdy moduł jest włączony i aktywny. Każda ramka = 12B header + payload (maks. 232B payload, łącznie maks. 244B przy MTU=247).

Header (12B)

Offset Rozmiar Pole Format Opis
0 1B module_id uint8_t ID modułu źródłowego
1 1B flags uint8_t Flagi (patrz niżej)
2 1B frame_number uint8_t Numer ramki (0–255, wrap)
3 1B sample_count uint8_t Liczba próbek w payloadzie
4 5B timestamp_first uint40_t LE Znacznik czasu pierwszej próbki [µs]
9 3B timestamp_delta uint24_t LE Różnica czasu do ostatniej próbki [µs]

Flagi ramki:

Bit Maska Nazwa Opis
7 0x80 FLAG_ERROR Ramka błędu — payload to 9B error_t (patrz sekcja 5)
0 0x01 Moduł-specyficzna Znaczenie zależy od modułu (patrz opisy modułów)

Pomocnik — int24 little-endian (TypeScript)

function readInt24LE(view: DataView, offset: number): number {
  const lo = view.getUint8(offset);
  const mi = view.getUint8(offset + 1);
  const hi = view.getInt8(offset + 2); // sign-extended
  return lo | (mi << 8) | (hi << 16);
}

5. Ramka błędu (FLAG_ERROR = 1)

Gdy flags & 0x80 != 0, payload = 9B struktura error_t:

Offset Rozmiar Pole Format Opis
0 1B level uint8_t Poziom błędu
1 1B source uint8_t Moduł źródłowy
2 1B reason uint8_t Kod przyczyny
3 1B instance uint8_t Instancja (np. numer sensora)
4 1B sensor_model uint8_t Model sensora (opcjonalne)
5 1B detail_type uint8_t Typ detalu
6 3B detail_value uint24_t LE Wartość detalu

6. Payloady modułów

6.1 ACC_GYRO (ID = 2) — Akcelerometr + Żyroskop

Rozmiar próbki: 18B (6 osi × 3B int24 little-endian signed)

Offset Rozmiar Pole Jednostka Przelicznik
0 3B acc_x deci-milli-g ÷ 10000 → g
3 3B acc_y deci-milli-g ÷ 10000 → g
6 3B acc_z deci-milli-g ÷ 10000 → g
9 3B gyro_x milli-°/s ÷ 1000 → °/s
12 3B gyro_y milli-°/s ÷ 1000 → °/s
15 3B gyro_z milli-°/s ÷ 1000 → °/s

Rozdzielczość: acc = 0.1 mg (0.0001 g), gyro = 1 milli-°/s (0.001 °/s). Domyślne zakresy: ±32 g, ±1000 dps. ODR domyślne: 240 Hz.

function parseAccGyroSample(view: DataView, offset: number) {
  return {
    accX:  readInt24LE(view, offset + 0)  / 10000, // g
    accY:  readInt24LE(view, offset + 3)  / 10000,
    accZ:  readInt24LE(view, offset + 6)  / 10000,
    gyroX: readInt24LE(view, offset + 9)  / 1000,  // °/s
    gyroY: readInt24LE(view, offset + 12) / 1000,
    gyroZ: readInt24LE(view, offset + 15) / 1000,
  };
}

6.2 BAROMETER (ID = 3) — Barometr

Flags bit 0 = MULTI_SENSOR: 0 = pojedynczy czujnik (5B/próbka), 1 = 4 czujniki (20B/próbka)

Pojedynczy czujnik — 5B/próbka:

Offset Rozmiar Pole Jednostka Przelicznik
0 3B pressure centipaskal [cPa] ÷ 100 → hPa
3 2B temperature 0.01°C ÷ 100 → °C

pressure = uint24_t LE (unsigned), temperature = int16_t LE (signed).

4 czujniki (Base Station) — 20B/próbka: 4× powtórzony format 5B.

6.3 BATTERY (ID = 4) — Napięcie baterii

Rozmiar próbki: 2B

Offset Rozmiar Pole Format Opis
0 2B voltage uint16_t LE Napięcie [mV]

6.4 GNSS (ID = 5) — Pozycja GPS/RTK

Rozmiar próbki: 55B (gnss_pvt_data_t, little-endian)

Offset Rozmiar Pole Format Opis
0 4B lat int32_t LE Szerokość geogr. [×10⁻⁷ °]
4 4B lon int32_t LE Długość geogr. [×10⁻⁷ °]
8 4B alt_msl int32_t LE Wysokość nad poziomem morza [mm]
12 4B vel_n int32_t LE Prędkość North [mm/s]
16 4B vel_e int32_t LE Prędkość East [mm/s]
20 4B vel_d int32_t LE Prędkość Down [mm/s]
24 4B head_mot int32_t LE Kierunek ruchu [×10⁻⁵ °]
28 4B h_acc uint32_t LE Dokładność pozioma [mm]
32 4B v_acc uint32_t LE Dokładność pionowa [mm]
36 4B geoid_sep int32_t LE Separacja geoidy [mm]
40 4B utc_nano int32_t LE Ułamek sekundy UTC [ns]
44 2B pdop uint16_t LE pDOP [×0.01]
46 2B hdop uint16_t LE hDOP [×0.01]
48 1B fix_type uint8_t 0=none, 2=2D, 3=3D, 4=GNSS, 5=Time
49 1B num_sv uint8_t Liczba satelitów
50 1B flags uint8_t bit0=gnssFixOK, bit1=diffSoln, bit2–3=carrSoln (0=none,1=float,2=fixed)
51 1B utc_hour uint8_t Godzina UTC
52 1B utc_min uint8_t Minuty UTC
53 1B utc_sec uint8_t Sekundy UTC
54 1B gnss_state uint8_t Stan wewnętrzny modemu GNSS

6.5 MAGNETOMETER (ID = 6) — Magnetometr

Rozmiar próbki: 9B (3 osie × 3B int24 little-endian signed)

Flags bit 0 = CALIBRATED: 0 = dane raw (bez korekcji hard/soft iron), 1 = dane skalibrowane.

Offset Rozmiar Pole Jednostka Przelicznik
0 3B mag_x nanoTesla ÷ 1000 → µT
3 3B mag_y nanoTesla ÷ 1000 → µT
6 3B mag_z nanoTesla ÷ 1000 → µT

Rozdzielczość: 1 nT (0.001 µT). Typowe ziemskie pole: 40 000–60 000 nT.

function parseMagSample(view: DataView, offset: number, flags: number) {
  return {
    x_uT:        readInt24LE(view, offset + 0) / 1000,
    y_uT:        readInt24LE(view, offset + 3) / 1000,
    z_uT:        readInt24LE(view, offset + 6) / 1000,
    isCalibrated: (flags & 0x01) !== 0,
  };
}

6.6 SELECTOR (ID = 7) — Selektor trybu ognia

Rozmiar próbki: 1B. Ramka wysyłana przy każdej zmianie pozycji.

Wartość Pozycja
1 SAFE
2 SEMI
3 AUTO

6.7 TRIGGER (ID = 8) — Trigger

Rozmiar próbki: 1B. Ramka wysyłana przy każdej zmianie stanu.

Wartość Stan
1 PRESSED
2 RELEASED

6.8 DISPLACEMENT (ID = 13) — Estymacja przemieszczenia

Rozmiar próbki: 12B (3 osie × 4B int32 little-endian signed)

Offset Rozmiar Pole Jednostka Przelicznik
0 4B pos_x mm ÷ 1000 → m
4 4B pos_y mm ÷ 1000 → m
8 4B pos_z mm ÷ 1000 → m

Pozycja jest względna do ostatniego resetu (KEY_DISP_RESET). Rozdzielczość: 1 mm.

function parseDisplacementSample(view: DataView, offset: number) {
  return {
    x_m: view.getInt32(offset + 0, true) / 1000,
    y_m: view.getInt32(offset + 4, true) / 1000,
    z_m: view.getInt32(offset + 8, true) / 1000,
  };
}

6.9 QUATERNION (ID = 9) — Fuzja orientacji (Madgwick)

Rozmiar próbki: 9B (4 komponenty Q15 + bajt statusu)

Offset Rozmiar Pole Format Opis
0 2B w int16_t LE Format Q1.15: ÷ 32768 → float
2 2B x int16_t LE
4 2B y int16_t LE
6 2B z int16_t LE
8 1B status uint8_t Bitmaska statusu fuzji (patrz niżej)

Bajt statusu fuzji:

Bit Maska Nazwa Znaczenie
0 0x01 MARG 1 = tryb MARG (mag+acc+gyro); 0 = IMU (acc+gyro only)
1 0x02 MAG_ENABLED 1 = magnetometr aktywny (próbka <2 s temu)
2 0x04 MAG_CALIBRATED 1 = skalibrowane próbki mag odbierane
3 0x08 MAG_VALID 1 = norma pola w zakresie 40–60 µT
4 0x10 MAG_FRESH 1 = ostatnia skalibrowana próbka <30 ms temu

Diagnostyka gdy MARG=0:

MAG_ENABLED MAG_CALIBRATED MAG_VALID MAG_FRESH Przyczyna
0 Magnetometr wyłączony
1 0 Brak kalibracji magnetometru
1 1 0 Zakłócenie pola (norma poza 40–60 µT)
1 1 1 0 Dane nieświeże (>30 ms)

Yaw z kwaternionu (tylko gdy MARG=1 — odniesiony do północy magnetycznej):

yaw_rad = atan2(2·(w·z + x·y),  1 − 2·(y² + z²))

function parseQuaternionSample(view: DataView, offset: number) {
  const toFloat = (raw: number) => raw / 32768;
  return {
    w:      toFloat(view.getInt16(offset + 0, true)),
    x:      toFloat(view.getInt16(offset + 2, true)),
    y:      toFloat(view.getInt16(offset + 4, true)),
    z:      toFloat(view.getInt16(offset + 6, true)),
    status: view.getUint8(offset + 8),
  };
}

function getYawDeg(q: { w: number; x: number; y: number; z: number }): number {
  const yawRad = Math.atan2(2 * (q.w * q.z + q.x * q.y),
                            1 - 2 * (q.y * q.y + q.z * q.z));
  return yawRad * 180 / Math.PI;
}

7. Protokół Control (Charakterystyka a202 — WRITE + INDICATE)

Klient wysyła komendy Write na charakterystykę Control. Urządzenie odpowiada Indicate na tę samą charakterystykę.

Format SET (Klient → Urządzenie)

[transaction_id][module_id][1 = SET][key][value_0 .. value_n]
Offset Rozmiar Pole Opis
0 1B transaction_id Dowolna wartość 0–255, echo w odpowiedzi
1 1B module_id ID modułu docelowego
2 1B command 1 = SET
3 1B key Klucz parametru
4+ 0–239B value Wartość (zależy od klucza)

Format SET Reply (Urządzenie → Klient)

[transaction_id][module_id][1][key][status]
Offset Rozmiar Pole Opis
0–3 4B nagłówek Echo z komendy
4 1B status Kod statusu (patrz niżej)

Format GET (Klient → Urządzenie)

[transaction_id][module_id][2 = GET][key]

Bez pola value.

Format GET Reply (Urządzenie → Klient)

[transaction_id][module_id][2][key][status][value_0 .. value_n]
Offset Rozmiar Pole Opis
0–3 4B nagłówek Echo z komendy
4 1B status Kod statusu
5+ 0–239B value Wartość (tylko gdy status = OK)

Kody statusu Control

Wartość Nazwa Opis
0x01 OK Sukces
0x02 UNKNOWN_ERROR Nieznany błąd
0x03 NOT_CALIBRATED Brak kalibracji
0x04 UNKNOWN_COMMAND Nieznana komenda
0x05 UNSUPPORTED_KEY Klucz nieobsługiwany przez ten moduł
0x06 INVALID_VALUE Nieprawidłowa wartość
0x07 BUSY Moduł zajęty (np. kalibracja w toku)
0x08 NOT_READY Nie gotowy (brak wymaganych danych)
0x09 HARDWARE_ERROR Błąd sprzętowy

8. Klucze Control — Uniwersalne (0x01–0x2F)

Identyczna semantyka niezależnie od modułu.

Lifecycle (0x01–0x0F)

Key Wartość Typ Rozmiar value Opis
KEY_CTRL_INFO 0x01 GET Informacje o module: nazwa modelu + TLV obsługiwanych ODR/zakresów.
KEY_CTRL_ENABLE 0x02 SET/GET 1B uint8_t 1 = włącz moduł, 0 = wyłącz.

KEY_CTRL_INFO — format odpowiedzi:

[model_name (null-terminated string)][TLV records...]
Każdy rekord TLV: [key (1B)][count (1B)][value[0]..value[count-1] (2B each, uint16 LE)] Przykładowe klucze w TLV: 0x10 = dostępne ODR, 0x11 = dostępne zakresy Range 1, 0x12 = zakresy Range 2, 0x14 = dostępne bandwidth filter.

Konfiguracja (0x10–0x1F)

Key Wartość Typ Rozmiar value Opis
KEY_CFG_ODR 0x10 SET/GET 2B uint16_t LE Częstotliwość próbkowania [Hz]
KEY_CFG_MEASURE_RANGE_1 0x11 SET/GET 2B uint16_t LE Zakres pomiaru 1 (np. acc [g])
KEY_CFG_MEASURE_RANGE_2 0x12 SET/GET 2B uint16_t LE Zakres pomiaru 2 (np. gyro [dps])
KEY_CFG_OVERSAMPLING 0x13 SET/GET 2B uint16_t LE Oversampling ratio
KEY_CFG_FILTER 0x14 SET/GET 2B uint16_t LE Bandwidth filtra [Hz]
KEY_CFG_RAW_MODE 0x15 SET/GET 1B uint8_t 0 = dane kalibrowane, 1 = surowe. SET 0 bez kalibracji → NOT_READY.

Kalibracja (0x20–0x2F)

Key Wartość Typ Rozmiar value Opis
KEY_CALIB_START 0x20 SET 0B Rozpocznij pomiar kalibracyjny. BUSY jeśli trwa.
KEY_CALIB_ABORT 0x21 SET 0B Przerwij aktywny pomiar.
KEY_CALIB_CAPTURE 0x22 SET (Nieużywane przez większość modułów)
KEY_CALIB_COMMIT 0x23 SET 0B Zatwierdź punkt — zapisz do NVS. NOT_READY jeśli brak danych.
KEY_CALIB_GET_BLOB 0x24 GET Odczytaj blob kalibracyjny (rozmiar zależy od modułu)
KEY_CALIB_SET_BLOB 0x25 SET (rozmiar bloba) Importuj blob kalibracyjny. Auto-save do NVS.
KEY_CALIB_STATUS 0x26 GET Status kalibracji (format zależy od modułu)

9. Klucze Control — Moduł-specyficzne (0x80–0xFF)

Wartości 0x80+ mają różne znaczenia dla różnych modułów — rozróżnia je pole module_id.

ACC_GYRO (module_id = 2)

Key Wartość Typ Rozmiar value Opis
KEY_AG_GET_TEMPERATURE 0x80 GET 2B reply Temperatura chipa IMU: int16_t LE [decidegC = 0.1°C]
KEY_AG_DELETE_CALIB_POINT 0x81 SET 2B Usuń punkt kalibracyjny: T = int16_t LE [decidegC]. Brak punktu → INVALID_VALUE.

GNSS (module_id = 5)

Key Wartość Typ Rozmiar value Opis
KEY_GNSS_UPDATE_RATE 0x80 SET/GET 2B uint16_t LE Częstotliwość aktualizacji [Hz]
KEY_GNSS_DYNAMIC_MODEL 0x81 SET/GET 1B uint8_t Model dynamiki odbiornika

TRIGGER (module_id = 8)

Key Wartość Typ Opis
KEY_TRIGGER_SENSITIVITY 0x80 SET/GET Czułość triggera
KEY_TRIGGER_MODE 0x81 SET/GET Tryb detekcji
KEY_TRIGGER_BASES 0x82 SET/GET Baza pomiaru

MAGNETOMETER (module_id = 6)

Key Wartość Typ Rozmiar value Opis
KEY_MAG_NORM_TOL 0x80 SET/GET 2B uint16_t LE Tolerancja normy pola [nT]. Domyślnie 10000 (10 µT). Okno akceptacji: [norm_ref − tol, norm_ref + tol]. SET → NVS.
KEY_MAG_NORM_REF 0x81 GET 4B uint32_t LE Bieżąca norma referencyjna [nT]. Domyślnie 50000 (50 µT). Aktualizowana automatycznie po imporcie kalibracji.

SELECTOR (module_id = 7)

Key Wartość Typ Opis
KEY_SELECTOR_HYSTERESIS 0x80 SET/GET Histereza detekcji pozycji (uint8_t, wartość Manhattan |dy|+|dz|)
KEY_SELECTOR_RAW_DATA 0x81 GET Bieżące surowe dane TMAG: value[0] = y (int8_t), value[1] = z (int8_t). Wymaga MODULE_ENABLE=true.

KICKBACK (module_id = 12)

Key Wartość Typ Opis
KEY_KICKBACK_TIMING 0x80 SET/GET Parametry czasowe: value[0] = on_time_ms (uint8_t), value[1] = off_time_ms (uint8_t)
KEY_KICKBACK_TEST 0x81 SET Uruchom jeden cykl testowy (brak value). BUSY jeśli cykl w toku.

DISPLACEMENT (module_id = 13)

Key Wartość Typ Rozmiar value Opis
KEY_DISP_RESET 0x80 SET 0B Reset KF: pozycja → 0, prędkość → 0, acc_bias → 0. Nowe origin = bieżąca pozycja.
KEY_DISP_SOFT_R 0x81 SET/GET 2B uint16_t LE Siła soft constraint prędkości. Jednostka: 0.1 m²/s². 0 = wyłączony. Domyślnie 40 (= 4.0 m²/s²). Większe → słabsza korekcja. Zapisywane do NVS.

10. Kalibracja biasu żyroskopu (ACC_GYRO — wielopunktowa temperaturowa)

Firmware obsługuje do 10 punktów kalibracyjnych, posortowanych rosnąco po temperaturze. Interpolacja liniowa między punktami, ekstrapolacja poza zakres (slope z krawędziowych punktów). Kalibracja jest przezroczysta — firmware automatycznie odejmuje bias od danych żyroskopu.

Kiedy kalibrować

Mierz w różnych temperaturach (minimalna odległość: 5°C między punktami, weryfikacja po stronie klienta). Urządzenie powinno leżeć nieruchomo przez cały czas pomiaru (~20 s).

Sekwencja dodania punktu

1. Ustabilizuj temperaturę
2. Control SET: KEY_CALIB_START (module_id=2, brak value)
3. Polluj Control GET: KEY_CALIB_STATUS co ~500 ms
   → sprawdź byte[1] (postęp 0–100%)
   → sprawdź byte[2-3] (bieżąca temperatura)
4. Gdy byte[1] = 100:
   Control SET: KEY_CALIB_COMMIT (module_id=2, brak value)
5. Control GET: KEY_CALIB_GET_BLOB → odśwież listę punktów w UI

KEY_CALIB_STATUS — format odpowiedzi GET (4–16B)

Offset Rozmiar Pole Opis
0 1B count Liczba zatwierdzonych punktów (0–10)
1 1B byte1 Jeśli pomiar aktywny: postęp 0–100%. Jeśli nieaktywny: raw_mode (0/1).
2 2B T_now Temperatura chipa: int16_t LE [decidegC]
4 12B avg_bias Opcjonalne — tylko gdy pomiar aktywny i są próbki. 3× int32_t LE [milli-°/s]: bias X, Y, Z

Blob kalibracyjny — format (164B)

Struktura ag_calib_nvs_t — serializacja do/z NVS przez GET_BLOB/SET_BLOB:

Offset Rozmiar Pole Format Opis
0 1B count uint8_t Liczba ważnych punktów (0–10)
1 3B reserved Ignorować
4 160B points[10] patrz niżej Tablica 10 punktów (indices ≥ count zawierają zera)

Każdy punkt (ag_bias_point_t, 16B):

Offset w punkcie Rozmiar Pole Format Opis
0 2B T int16_t LE Temperatura [decidegC]
2 2B _pad Wyrównanie — ignorować
4 4B bias_x int32_t LE Bias osi X [milli-°/s]
8 4B bias_y int32_t LE Bias osi Y [milli-°/s]
12 4B bias_z int32_t LE Bias osi Z [milli-°/s]

Punkty zawsze posortowane rosnąco po T.

interface GyroBiasPoint {
  tempDegC: number;
  biasX_dps: number;
  biasY_dps: number;
  biasZ_dps: number;
}

function parseGyroBiasBlob(data: DataView): GyroBiasPoint[] {
  const count = Math.min(data.getUint8(0), 10);
  const points: GyroBiasPoint[] = [];
  for (let i = 0; i < count; i++) {
    const base = 4 + i * 16;
    points.push({
      tempDegC:  data.getInt16(base, true) / 10,
      biasX_dps: data.getInt32(base + 4, true) / 1000,
      biasY_dps: data.getInt32(base + 8, true) / 1000,
      biasZ_dps: data.getInt32(base + 12, true) / 1000,
    });
  }
  return points;
}

Usunięcie punktu kalibracyjnego

Control SET: KEY_AG_DELETE_CALIB_POINT (0x81), module_id=2
value[0-1] = T (int16_t LE, decidegC)

Przykład: usuń punkt 25.0°C (= 250 decidegC):

TX: [txId][0x02][0x01][0x81][0xFA][0x00]
RX: [txId][0x02][0x01][0x81][0x01]  ← OK

Odczyt temperatury IMU (bez kalibracji)

TX: [txId][0x02][0x02][0x80]
RX: [txId][0x02][0x02][0x80][0x01][0xFA][0x00]  ← OK, 250 = 25.0°C

11. Kalibracja magnetometru (hard-iron + soft-iron)

Kalibracja wykonywana jest po stronie klienta (aplikacja mobilna). Firmware tylko przechowuje i stosuje blob — nie ma interaktywnej sekwencji CALIB_START.

Procedura kalibracji: 1. Pobierz surowe dane mag (Control SET: KEY_CFG_RAW_MODE = 1) 2. Obracaj urządzenie w różnych kierunkach (opisuj sferę) 3. Oblicz hard-iron i soft-iron po stronie klienta (algorytm elipsoidy) 4. Wyślij blob: Control SET KEY_CALIB_SET_BLOB (42B) 5. Firmware zapamiętuje do NVS i wyłącza raw mode

Blob kalibracyjny magnetometru — format (42B)

Offset Rozmiar Pole Format Jednostka
0 2B hard_iron_x int16_t LE deci-µT (1 LSB = 0.1 µT)
2 2B hard_iron_y int16_t LE deci-µT
4 2B hard_iron_z int16_t LE deci-µT
6 4B soft_iron[0][0] float32 LE bezwymiarowa
10 4B soft_iron[0][1] float32 LE bezwymiarowa
14 4B soft_iron[0][2] float32 LE bezwymiarowa
18 4B soft_iron[1][0] float32 LE bezwymiarowa
22 4B soft_iron[1][1] float32 LE bezwymiarowa
26 4B soft_iron[1][2] float32 LE bezwymiarowa
30 4B soft_iron[2][0] float32 LE bezwymiarowa
34 4B soft_iron[2][1] float32 LE bezwymiarowa
38 4B soft_iron[2][2] float32 LE bezwymiarowa

Uwaga: hard-iron w blobie jest w deci-µT (int16_t), natomiast dane w ramce Data są w nT (int24) — to dwie różne reprezentacje.

Domyślne wartości (brak kalibracji): hard_iron = {0, 0, 0}, soft_iron = macierz jednostkowa.

function buildMagCalibBlob(hard_iron_uT: [number,number,number], soft_iron: number[][]): ArrayBuffer {
  const buf = new ArrayBuffer(42);
  const v = new DataView(buf);
  v.setInt16(0, Math.round(hard_iron_uT[0] * 10), true); // µT → deci-µT
  v.setInt16(2, Math.round(hard_iron_uT[1] * 10), true);
  v.setInt16(4, Math.round(hard_iron_uT[2] * 10), true);
  for (let r = 0; r < 3; r++)
    for (let c = 0; c < 3; c++)
      v.setFloat32(6 + (r * 3 + c) * 4, soft_iron[r][c], true);
  return buf;
}

CALIB_STATUS magnetometru — odpowiedź GET (1B)

value[0] Znaczenie
0 Brak kalibracji
1 Skalibrowany, tryb calibrated
2 Skalibrowany, tryb raw

12. RTCM Stream (Charakterystyka a204 — Write Without Response)

Służy do przesyłania korekcji RTK do modemu GNSS (format RTCM 3.x). Strumień należy fragmentować do rozmiarów MTU (max 512B/pakiet). Urządzenie przekazuje dane bezpośrednio do odbiornika u-blox ZED-F9P przez UART.


13. Przykłady kompletnych transakcji

Włączenie modułu Magnetometer

TX: [0x01][0x06][0x01][0x02][0x01]
     txId   mag   SET  ENABLE  1

RX: [0x01][0x06][0x01][0x02][0x01]
     txId   mag   SET  ENABLE  OK

Odczyt statusu kalibracji magnetometru

TX: [0x02][0x06][0x02][0x26]
     txId   mag   GET  CALIB_STATUS

RX: [0x02][0x06][0x02][0x26][0x01][0x01]
     txId   mag   GET  CALIB_STATUS  OK  calibrated

Odczyt bloba kalibracji żyroskopu

TX: [0x03][0x02][0x02][0x24]
     txId   ag   GET  CALIB_GET_BLOB

RX: [0x03][0x02][0x02][0x24][0x01][164 bajty danych]
     txId   ag   GET  CALIB_GET_BLOB  OK  blob

Rozpoczęcie pomiaru kalibracyjnego żyroskopu

TX: [0x04][0x02][0x01][0x20]
     txId   ag   SET  CALIB_START   (brak value)

RX: [0x04][0x02][0x01][0x20][0x01]
     txId   ag   SET  CALIB_START   OK

Polling statusu podczas kalibracji

TX: [0x05][0x02][0x02][0x26]

RX: [0x05][0x02][0x02][0x26][0x01][0x02][0x01][0x32][0xF4][01 02 03 04 05 06 07 08 09 0A 0B 0C]
                                          count  prog  T_now(little-endian)    avg_bias (12B)
     count=2 istniejące punkty, progress=1%, T=0x01F4=500 decidegC=50.0°C, avg_bias opcjonalne

Zatwierdzenie punktu kalibracyjnego

TX: [0x06][0x02][0x01][0x23]
     txId   ag   SET  CALIB_COMMIT  (brak value)

RX: [0x06][0x02][0x01][0x23][0x01]
     txId   ag   SET  CALIB_COMMIT  OK