MAX30102

MAX30102. 

 Датчик сердечного ритма.






 
ESP32 > MAX30102
 GND > GND
 3V3 > VCC
 D5 > SCL
 D6 > SDA
 

Вывод : NO FINGER.  
RED: 49172  IR: 38468  SpO₂: 99. 


main.py     

from machine import Pin, I2C
import time
from max30102 import MAX30102
from biosensor import IIRFilter, FingerDetect, SpO2Calculator

#from led_driver import print_like_console , print_multiline_console

i2c = I2C(0, scl=Pin(5), sda=Pin(6), freq=400000)
sensor = MAX30102(i2c)

finger = FingerDetect()
spo2_calc = SpO2Calculator()

print("READY")

while True:
    red, ir = sensor.read_fifo()

    # 1️⃣ авто-детект пальца
    if not finger.update(ir):
        print("NO FINGER")
        #print_like_console('NO FINGER', x=0, y=0, delay=0.005)
        time.sleep(0.1)
        continue

    # 2️⃣ SpO2
    spo2 = spo2_calc.update(red, ir)

    print("RED:", red, " IR:", ir,
          " SpO₂:", int(spo2) if spo2 else "...")
    #print_like_console(f" SpO: {spo2}", x=0, y=0, delay=0.005)

    time.sleep(0.05)



max30102.py     

# max30102.py  — MicroPython версия
from machine import I2C
import time

# register addresses
REG_INTR_STATUS_1   = 0x00
REG_INTR_STATUS_2   = 0x01
REG_INTR_ENABLE_1   = 0x02
REG_INTR_ENABLE_2   = 0x03
REG_FIFO_WR_PTR     = 0x04
REG_OVF_COUNTER     = 0x05
REG_FIFO_RD_PTR     = 0x06
REG_FIFO_DATA       = 0x07
REG_FIFO_CONFIG     = 0x08
REG_MODE_CONFIG     = 0x09
REG_SPO2_CONFIG     = 0x0A
REG_LED1_PA         = 0x0C
REG_LED2_PA         = 0x0D
REG_PILOT_PA        = 0x10


class MAX30102:

    def __init__(self, i2c, address=0x57):
        self.i2c = i2c
        self.address = address

        self.reset()
        time.sleep(1)

        # clear interrupt flags
        self._read(REG_INTR_STATUS_1)
        self._read(REG_INTR_STATUS_2)

        self.setup()

    # -------------------
    # LOW LEVEL FUNCTIONS
    # -------------------

    def _write(self, reg, value):
        self.i2c.writeto_mem(self.address, reg, bytes([value]))

    def _read(self, reg, n=1):
        return self.i2c.readfrom_mem(self.address, reg, n)

    # -------------------
    # DEVICE CONTROL
    # -------------------

    def shutdown(self):
        self._write(REG_MODE_CONFIG, 0x80)

    def reset(self):
        self._write(REG_MODE_CONFIG, 0x40)

    def setup(self, led_mode=0x03):
        # enable interrupts
        self._write(REG_INTR_ENABLE_1, 0xC0)
        self._write(REG_INTR_ENABLE_2, 0x00)

        # FIFO
        self._write(REG_FIFO_WR_PTR, 0x00)
        self._write(REG_OVF_COUNTER, 0x00)
        self._write(REG_FIFO_RD_PTR, 0x00)
        self._write(REG_FIFO_CONFIG, 0x4F)  # avg=4, almost full=17

        # MODE — SpO2 (RED + IR)
        self._write(REG_MODE_CONFIG, led_mode)

        # SPO2 config: ADC range=4096, sample rate=100Hz, pulse width=411us
        self._write(REG_SPO2_CONFIG, 0x27)

        # LED amplitudes
        self._write(REG_LED1_PA, 0x24)   # ~7mA IR
        self._write(REG_LED2_PA, 0x24)   # ~7mA RED
        self._write(REG_PILOT_PA, 0x7F)  # ~25mA

    # -------------------
    # READING
    # -------------------

    def get_data_present(self):
        read_ptr = self._read(REG_FIFO_RD_PTR)[0]
        write_ptr = self._read(REG_FIFO_WR_PTR)[0]
        n = write_ptr - read_ptr
        if n < 0:
            n += 32
        return n

    def read_fifo(self):
        # clear interrupts
        self._read(REG_INTR_STATUS_1)
        self._read(REG_INTR_STATUS_2)

        d = self._read(REG_FIFO_DATA, 6)

        red = (d[0] << 16 | d[1] << 8 | d[2]) & 0x03FFFF
        ir  = (d[3] << 16 | d[4] << 8 | d[5]) & 0x03FFFF

        return red, ir

    def read_sequential(self, amount=100):
        red_buf = []
        ir_buf = []

        count = amount

        while count > 0:
            n = self.get_data_present()
            while n > 0:
                red, ir = self.read_fifo()
                red_buf.append(red)
                ir_buf.append(ir)
                n -= 1
                count -= 1

        return red_buf, ir_buf


biosensor.py     

import time

class IIRFilter:
    def __init__(self, alpha=0.1):
        self.alpha = alpha
        self.value = None

    def add(self, x):
        if self.value is None:
            self.value = x
        else:
            self.value = self.alpha * x + (1 - self.alpha) * self.value
        return self.value


class FingerDetect:
    def __init__(self):
        self.present = False
        self.threshold = 20000  # автопорог

    def update(self, ir_value):
        # адаптивный порог: помним среднее
        if ir_value < 10000:
            self.present = False
        elif ir_value > 30000:
            self.present = True

        return self.present


class SpO2Calculator:
    def __init__(self):
        self.red_dc = IIRFilter(0.01)
        self.ir_dc  = IIRFilter(0.01)

        self.red_ac = IIRFilter(0.7)
        self.ir_ac  = IIRFilter(0.7)

        self.spO2 = None

    def update(self, red, ir):
        # обновляем DC компоненты
        red_dc_val = self.red_dc.add(red)
        ir_dc_val  = self.ir_dc.add(ir)

        # AC = текущее - DC
        red_ac_val = self.red_ac.add(red - red_dc_val)
        ir_ac_val  = self.ir_ac.add(ir - ir_dc_val)

        # R = (AC_red/DC_red) / (AC_ir/DC_ir)
        if ir_dc_val > 0 and red_dc_val > 0 and ir_ac_val != 0:
            R = (red_ac_val / red_dc_val) / (ir_ac_val / ir_dc_val)
            spo2 = 104 - 17 * R

            # фильтр результата
            if 70 < spo2 < 100:
                self.spO2 = spo2

        return self.spO2



Комментарии

Популярные сообщения из этого блога

Установка micropython на ESP32

LORA Приемник и передатчик

LORA Upgrade E32 400M30S