fbpx

Prawdopodobnie każdy mój czytelnik miał do czynienia z jakimś urządzeniem ubieralnym tzw. wearables. Nawet najprostsze opaski fitnesowe mają wbudowany czujnik tętna, który dokonuje pomiaru najczęściej na nadgarstku osoby użytkującej taki gadżet. Okazuje się, że nie jest to tajemnicza technologia, która jest dostępna jedynie dla zamkniętego grona producentów elektroniki użytkowej. Również na naszych biurkach możemy dokonać prostego pomiaru własnego tętna. Wystarczy do tego odpowiedni układ.

MAX30102 w służbie zdrowiu

Firma Maxim wypuściła kilka sensorów służących pomiarom tętna. Jednym z nich jest omawiany dzisiaj MAX30102 ( MAX30102 Datasheet ). Jest to w zasadzie urządzenie 2 w 1 bowiem oprócz pulsu można nim mierzyć również saturację, czyli natlenienie krwi. Czujnik ten jest chętnie wykorzystywany w smartfonach , na przykład w Samsungach.

Sam czujnik do zasilania logiki wymaga napięcia 1,8 V. Opróćz tego wymagane jest również drugie napięcie do zasilenia diod. Może to być 3,3 V, ale diody spokojnie wytrzymują też 5 V. Moduł posiada wbudowane zasilanie diod właśnie na poziomie 5V, więc nie trzeba się nim niepotrzebnie martwić.

Czujnik ten posiada wbudowane dwie diody LED oświetlające badaną tkankę – czerwoną oraz podczerwoną. Do zbierania sygnału służy natomiast fotodetektor.

Układ cechuje się bardzo niskim poborem prądu wynoszącym w trybie Shutdown jedyne 0,7 µA. Całościowy prąd pobierany przez czujnik w stanie aktywnym w głównej mierze uzależniony jest od prądu pobieranego przez diody, który jest regulowany programowo. Nie licząc diod sam czujnik do pracy potrzebuje około 600 µA. Prąd każdej z LEDek natomiast regulowany jest programowo w zakresie 0÷51 mA. Huh! 51 mA na diodę wydaje się cakiem sporo. Nie odważyłem się puścić aż tak dużego prądu…

Czujnik posiada wyjście przerwania INT. Może ono służyć do sygnalizacji kilku zdarzeń. Najprzydatniejsze z nich moim zdaniem to sygnalizacja nowej próbki oraz zbliżającego się przepełnienie FIFO.

Odbiciowy pomiar tętna i saturacji

Wykorzystywany przez naszego Maxa pomiar nazywa się odbiciowym dlatego, że diody oraz fotodetektor znajdują się po tej samej stronie palca. Światło emitowane przez diody odbija się od palca(a dokładniej hemoglobiny) i wraca na detektor.

Pomiar saturacji krwi odbywa się dzięki zjawisku pochłaniania światła przez hemoglobinę. Hemoglobina natleniona w niskim stopniu pochłania światło o długości fali około 660 nm, natomiast hemoglobina mocno natleniona pochłonie więcej światła o długości fali 940 nm. Diody wbudowane w układ MAX30102 posiadają długości fal 660 nm oraz 880 nm. Mimo, że nie trafiają idealnie w długość fali podczerwonej, to działa poprawnie. Poziom natlenienia wyznacza się za pomocą stosunku transmitancji światła dla obu tych długości.

Przebieg wartości odbitego światła jako główną składową zawiera falę tętna z której łatwo jest odczytać puls. Poniżej zamieściłem wykres dla diody czerwonej, który ściągnąłem z mojego palca omawianym czujnikiem.

Schemat i CubeMX

Dzisiaj wykorzystam Nucleo z układem STM32F401RE.

Schemat połączenia czujnika jest prosty za sprawą kompletnego modułu jaki kupujemy. Niestety pullupy na module są podłączone do 1,8 V co nie do końca chce współpracować z STM32 pędzącym na 3,3 V a przynajmniej z moim egzemplarzem. Dlatego dodałem zewnętrzene pullupy do napięcia zasilania modułu, a piny I2C oraz INT w mikrokontrolerze skonfigurowałem jako No pull-up and no pull-down.

I2C ustaw od razu na 400 kHz.

Do działania potrzebujesz jeszcze wejścia przerwania reagującego na zbocze opadające. Upewnij się, że Cube aktywował to przerwanie podczas inicjalizacji. Czasem bywają z tym jaja. Uprzedzając komentarze – tak, tak HAL jest najgorszy na świecie przez to 🙂

Biblioteka

Przechodzimy do najciekawszego czyli do tego, jak korzystać z naszego czujnika. Maxim na swojej stronie udostępnia przykładowy kod dla środowiska Arduino oraz mbed. O ile sam algorytm wyszukiwania szczytów w sygnale, przeliczania ich na tętno oraz algorytm liczący natlenienie krwi są ok, tak przykład zbierania próbek jest beznadziejny.

Niestety główna przykładowa pętla main zdominowana jest czekaniem na pojawienie się przerwania od czujnika. Wtedy pobierane i przeliczane są próbki. Po zebraniu najpierw 500, a później co każde 100 próbek uruchamiany jest algorytm. Próbki moim zdaniem niepotrzebnie są przesuwane w lewo po obliczeniach. Nie może być tak, że mikrokontroler specjalnie czeka na przerwanie! W dodatku cały czas diody świecą pełną mocą, nie ważne czy palec jest przyłożony do czujnika czy nie. Co za marnotrawienie prądu…

Przykład również nie będzie działał z inną konfiguracją czujnika niż 100 sampli na sekundę. Poprawiłem to.

Działanie mojej biblioteki oparłem na prostej maszynie stanowej z czterema stanami, gdzie w międzyczasie cały czas w przerwaniu zbierane są próbki. Próbki trafiają do bufora cyklicznego dzięki czemu nie trzeba ich przesuwać po każdym puszczeniu algorytmu. Co się dzieje w tych dzikich stanach maszynowych?

  1. MAX30102_STATE_BEGIN. Palec nie jest przyłożony do czujnika. Dioda czerwona jest wyłączona, IR na minimum. Dioda IR ma za zadanie wykryć palec położony na czujniku. Zawsze kiedy palec zostanie ściągnięty z czujnika, program wraca do tego stanu wygaszając diody.
  2. MAX30102_STATE_CALIBRATE. Po przyłożeniu palca należy zapełnić bufor ‘poprawnymi danymi’. Standardowo obliczenia wykonywane są na próbkach z ostatnich 5 sekund. Kalibracja, czyli zapełnienie bufora więc będzie tyle trwało.
  3. MAX30102_STATE_CALCULATE_HR. Po zebraniu odpowiedniej liczby próbek, dokonywane są obliczenia według algorytmu Maxima. W tym kroku przesunięty zostaje bufor cykliczny o ilości sampli odpowiadającej jednej sekundzie.
  4. MAX30102_STATE_COLLECT_NEXT_PORTION. W tym stanie obsługa czujnika znajduje się do czasu, kiedy zostanie zebrana kolejna porcja próbek, czyli z kolejnej sekundy. Po tym trafia do stanu numer 3 i cykl powtarza się aż do ściągnięcia palca z czujnika.

Używanie biblioteki

Bibliotekę pisałem tak, aby jej użycie było jak najprostsze. Całość opiera się na kilku krokach. Po pierwsze należy zainicjalizować bibliotekę i czujnik podając który intefejs I2C został użyty.

MAX30102_STATUS Max30102_Init(I2C_HandleTypeDef *i2c);

Kolejnym ważnym punktem jest obsługa przerwania. Należy przedefiniować wbudowaną w HALa funkcję weak.

/* USER CODE BEGIN 4 */
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
	if (GPIO_Pin == INT_Pin)
	{
		Max30102_InterruptCallback();
	}
}
/* USER CODE END 4 */

Ostatnim warunkiem jest wrzucenie do pętli głównej funkcji odpowiadającej za obsługę maszyny stanów.

void Max30102_Task(void);

Teraz możesz czytać z biblioteki bieżące wartości tętna oraz saturacji za pomocą dwóch funkcji.

int32_t Max30102_GetHeartRate(void);
int32_t Max30102_GetSpO2Value(void);

Dodatkowa konfiguracja

Biblioteka oraz czujnik standardowo skonfigurowane są na 5-sekundowy pomiar oraz 100 próbek na sekundę. Możesz to dowolnie modyfikować. Biblioteka automatycznie dobierze wielkości buforów na sample oraz będzie w stanie dostosować obliczenia.

Dodatkowo jest takie ustawienie jak ilość sampli dla wywołania przerwania FIFO Almost Full. Jeżeli z jakiegoś powodu próbki nie będą odbierane na czas, będą one kolejkowane w czujniku. Zkolejkowanie ustawionej liczby próbek wywoła przerwanie od tego zdarzenia. Biblioteka wykrywając te przerwanie, odczyta wszystkie dostępne próbki jakie znajdują się w buforze, czyszcząc go jednocześnie. Bufor maksymalnie pomieści 32 próbki. Domyślna wartość 17 jest wartością minimalną która wywołuje przerwanie. Jak dla mnie jest to wystarczające ustawienie lecz nie sprawdzałem czujnika w prawdziwym boju. Być może w rozbudowanej aplikacji czujnik będzie się lepiej spisywał przy wyższej wartości.

#define MAX30102_MEASUREMENT_SECONDS 5
#define MAX30102_SAMPLES_PER_SECOND 100 // 50, 100, 200, 400, 800, 100, 1600, 3200 sample rating
#define MAX30102_FIFO_ALMOST_FULL_SAMPLES 17

Prezentacja działania

Mój przykładowy kod printuje na terminal obliczane wartości.

Całkiem niskie tętno jak na siedzenie i klepanie kodu. A myślałem, że to stresujące zajęcie 🙂 Zobacz jeszcze jak działa automatyczne załączanie i wyłączanie diod w zależności czy do czujnika jest przyłożony palec.

Podsumowanie

Pulsometr od Maxim’a jest bardo ciekawym urządzeniem. Działa poprawnie lecz niestety czasem zdarzy mu się potnąć skacząc z wyliczonym tętnem. Nie jestem dobry z obróbki sygnałów – miałem 3 z laborek 🙁 Jakby znalazł się ktoś, kto zerknie w kod algoytmu i go poprawi to będę niesamowicie wdzięczny! Z drugiej strony w moim smartfonie wygląda to podobnie, więc być może nie jest to wina kulawego algorytmu oraz mojego programu.

Obecnie urządzenia ubieralne potrafią mierzyć tętno z nadgarstka. Niestety ten czujnik słabo sobie radzi w tym miejscu. Zaczerpnąłem trochę teorii i do pomiaru nadgarstkowego zalecana jest jeszcze jedna, zielona dioda. Widać to np. we wspomnianych na samym początku opaskach fitnesowych lub zegarkach biegowych. Myślę, że można potraktować ten czujnik – tak samo jak w telefonie – bardziej jako ciekawostkę niż precyzyjny przyrząd pomiarowy. Ciągły pomiar z palca przydatny może być jedynie w szpitalu. Sam dla siebie nie widzę większego zastosowania. Może masz jakieś? Podziel się tym w komentarzu!

Pełny projekt wraz z biblioteką znajdziesz jak zwykle na moim GitHubie: link

Jeśli zauważyłeś jakiś błąd, nie zgadzasz się z czymś, chciałbyś coś dodać istotnego lub po prostu uważasz, że chciałbyś podystkutować w tym temacie, napisz komentarz. Pamiętaj, że dyskusja ma być kulturalna i zgodna z zasadami języka polskiego.

Jeżeli czujnik Ci się spodobał, możesz nabyć go w moim sklepie.

Wyniki konkursu z poprzedniego wpisu

Ostatnio zrecenzowałem książkę “Mikrokontrolery STM32 dla początkujących”, która opiera się na używanych przeze mnie bibliotekach HAL. Z tej okazji zorganizowałem konkurs w którym do wygrania łącznie były 4 komplety książka + płytka ewaluacyjna KA-NUCLEO-F411CEv2. Nie przedłużając już oto wybrani przeze mnie zwycięzcy:

1. Szymon Fronc z komentarzem:

Recenzja taka jak powinna być treściwa i rzeczowa budująca głód i chęci do podjęcia przygody z czymś co dla kogoś takiego jak ja jest dalszą drogą zabawy z mikrokontrolerami.Myśle ze STM’y mogą poszerzyć horyzonty(coś nowego niż dotychczasowe arduino), czego chętnie bym spróbował jeśli udałoby mi się zgarnąć taki zestaw dla siebie. Pora wziąć udział w konkursie ? Pozdrawiam i czekam na kolejne ciekawe recenzje!

2.  Mateusz z komentarzem:

Dobra, rzeczowa recenzja, która przede wszystkim zachęca “gang świeżaków” do rozpoczęcia przygody z mikrokontrolerami STM i szczerze odradza książkę bardziej doświadczonym programistom. Ja sam od dłuższego czasu siedzę w mikrokontrolerach z rodziny AVR i muszę przyznać, że do STM zabierałem się jak pies do jeża! Fajnie, że znajdują się osoby, które zarażają swoją pasją innych. Dziękuję i pozdrawiam!

3. Franek z komentarzem:

Dobra recenzja, widać że autor faktycznie jest doświadczony w temacie oraz jest zainteresowany książką. Na duży plus również to, iż jest to recenzja szczera pokazująca książkę również od strony jej wad i wprost mówiąca dla jakich odbiorców jest ona przeznaczona

4. Norbert z komentarzem:

Recenzja z pewnością zachęciła mnie do wzięcia udziału w konkursie, być może przełoży się to na podejście do tego “potwora” jakim są ogromne możliwości mikrokontrolerów STM32. Jeśli przedstawisz przekrój począwszy od początkujących propozycji po te bardziej zaawansowane – jestem w pełni za takimi publikacjami.

Gratuluję! W celu sfinalizowania konkursu odezwę się do Was mailowo. Pozdro!

4.5/5 - (6 votes)

Podobne artykuły

.

23 komentarze

Marek · 26/11/2021 o 10:25

Cześć, miałeś może problem z tym, że dioda w ogóle nie świeciła? Kod widać że działa, tylko, że wysyła same zera z tego pewnie względu,że kompletnie nic z LEDem się nie dzieje. Mam podłączone tak jak na Twoim schemacie wszystko. Mam tą czarną płytkę.

FK · 25/03/2021 o 17:56

Cześć. Dzięki za ten świetny post. Otrzymałeś wiele przydatnych informacji.
Mam pytanie. Próbuję zrobić ten sam projekt dla nucleo-f446re również z MAX30102. Ale mam problem, nic się nie dzieje. Nie wiem, gdzie popełniłem błąd podczas przenoszenia.

Byłbym bardzo wdzięczny, gdybyś mógł mi pomóc.

Z poważaniem,
FK

    Mateusz Salamon · 25/03/2021 o 18:08

    Oprócz tego, że Ci nie działa to kompletnie nic nie wiem 🙁 Napisz mi na maila mateusz@msalamon.pl wszystko to, co zrobiłeś i jaki masz efekt. Bez tego mogę tylko wróżyć.

marville · 20/10/2020 o 15:39

Może głupie pytanie ale czy “zielone” płytki MAXa (7-pinów po jednej stronie) różnia się czymś od tych “ciemniejszych” 4 piny po jednej i 4 po drugiej. Pytam, ponieważ mam wszystko skonfigurowane tak jak u Ciebie, używam jedynie stmf411 i jedynie póki co kwestie hardwarowe wchodzą chyba w gre. No chyba, że czegoś nie zauwazyłem. Rezystory podciągające mam.

    marville · 20/10/2020 o 15:40

    Przede wszystkim nie określiłem problemu 😀 Wysyłane mam cały czas same ‘0’.

    Mateusz Salamon · 20/10/2020 o 15:49

    Nie, nie różnią się 😀 Genralnie ten czujnik sprawia masę problemów w uruchamianiu 🙂 Musisz szukać kto i gdzie daje te zero. Może I2C się nie dogaduje albo coś takiego.

      marville · 20/10/2020 o 15:53

      No właśnie podejrzewam I2C, ponieważ nic kompletnie nie widzę na liniach SDA i SCL, a chyba przynajmniej SCL powinien się jakoś zmieniać a tu totalna cisza.

      Mateusz Salamon · 20/10/2020 o 15:54

      Obydwa piny są w stanie wysokim? Może coś Ci ściąga SDA do zera i I2C myśli, że jest linia zajęta 🙂

      marville · 20/10/2020 o 15:59

      Patrząc na Logicu – tak, oba w stanie wysokim. Ok, zobaczę co by mogło mi ściągać do masy, chociaż nie używam tego I2C do niczego innego. Jedynie to tego czujniks używając Twoich bibliotek.

      P.S. Nie wiem czemu nie mogłem odpowiedzieć na poniższy Twój komentarz. Brak tego przycisku “Odpowiedź” 😛

      marville · 20/10/2020 o 16:01

      Czy jest jakaś dioda sygnalizująca w tych “ciemniejszych” płytkach taka jak na Twoich filmiku? W sensie czy jeżeli palec przyłoże do pulsometru to ma się zaświecić, bo póki co jedynie czerwona dioda cały czas mi się świeci ale wygląda jakby sygnalizowała poprostu uruchomiony czujnik.

      Mateusz Salamon · 20/10/2020 o 16:02

      Bo skończyła się liczba zagnieżdżeń. Tak to jest dziwnie skonstruowane, że trzeba odpowiadać do wątku wcześniej zagnieżdżonego 😛

      ,marville · 05/12/2021 o 22:33

      Ok, kompletnie nie wchodzi mi do przerwania. Nie wykrywa palca na czujniku i nie zmiania się status, dlatego wysyła zera. Jakiś pomysł co i jak sprawdzić?

      Mateusz Salamon · 06/12/2021 o 20:40

      No co? Trzeba szukać czemu nie wchodzi do przerwania 🙂 Czy czujnik nie wystawia, czy STM32 nie ustawionym, czy w ogóle I2C działa i ustawia czujnik. Wiesz jak ma działać, to wiesz jak szukać 🙂

Kamil · 07/09/2020 o 09:42

Błąd przy zmianie parametrów, to błąd kompilatora. Zauważyłem, że jest to najpewniej niewystarczająca pamięć, żeby obsłużyć nawet niewiele większą ilość próbek.

Co do wyniku -999, faktycznie czujnik jest średni jeśli chodzi o jakość sygnału. Poprawiłem to algorytmem, wycinając duże odchylenia i jest lepiej.

sara · 12/08/2020 o 20:40

hi,
I used this code in my system with 6 other sensors with all I2C4. But When I put this callback for MAX3010x the whole program goes to hanging and suddenly stops. Just by commenting the callback others seems OK. can you help me with this issue?

Kamil · 05/08/2020 o 19:37

Witam, spróbowałem zastosować udostępniony kod na Nucleo F103RB. Póki co napotkałem dwa problemy:
1) przy próbie zmiany wartości MAX30102_MEASUREMENT_SECONDS (na np.10) lub MAX30102_SAMPLES_PER_SECOND (na np. 200), program nie kompiluje się. Co może być tego przyczyną?
2) drugi problem to wynik SpO2 praktycznie zawsze pozostaje na -999. HR jest w miare poprawny. Nie ingerowałem w pliki algorithm.* oraz MAX30102.*
Pozdrawiam

    Mateusz Salamon · 06/08/2020 o 10:12

    Jaki błąd dostajesz przy zmianie tych parametrów?
    Ten wynik -999 spowodowany jest pewnie jakimiś śmieciami w pomiarach. Niestety ten czujnik nie jest jakiś porywający i musi mieć dosyć dobre warunki do pomiarów. W telefonie to widzę, bo gdy tylko ruszę lekko palec – pomiar wariuje.

saeideh · 07/06/2020 o 19:23

hello & THANK YOU FOR YOUR good explain but could you please tell me about wiring, I get confused about TEST and INT button and INT (MAX30102) and how to config them

    Mateusz Salamon · 07/06/2020 o 19:39

    Don’t care about TEST pin 🙂 It’s only for timing check purpose.

    INT is interrupt pin from MAX30102. Youi have to set EXTI interrupt pin on STM32, enable Interrupt for falling edge and write a callback for it.

Kursad · 16/04/2020 o 17:31

i tested your project with stm32f103cbu and stm32f411ce and
this module https://www.aliexpress.com/i/33019226882.html but it didnt work, pls help me about projrct , thanks for now

    Mateusz Salamon · 18/04/2020 o 15:48

    I don’t know what did you do in your project. You can send me a full description to mateusz@msalamon.pl. I will try to help you 🙂

Michsł · 10/04/2019 o 13:42

Świetny wpis, jak zawsze zresztą. Może czas na komunikację LoRa ;)?

    Mateusz Salamon · 11/04/2019 o 20:42

    Dzięki! Moduły LoRa leżą w szafie w szufladce “do zrobienia” więc pewnie w końcu się pojawią. Nie mam tylko cos do nich weny 🙁

Dodaj komentarz

Avatar placeholder

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *