vinay y.n
Published © GPL3+

BP Classification & announcing Results over Voice Using Pi3

Classification Of Blood Pressure, Spo2, Body Temperature Monitoring, and announcing sensor Results Over The Voice Using Raspberry Pi

AdvancedFull instructions providedOver 2 days300
BP Classification & announcing Results over Voice Using Pi3

Things used in this project

Hardware components

Raspberry Pi 3 Model B
Raspberry Pi 3 Model B
×1
MAX30102 High-Sensitivity Pulse Oximeter and Heart-Rate Sensor for Wearable Health
Maxim Integrated MAX30102 High-Sensitivity Pulse Oximeter and Heart-Rate Sensor for Wearable Health
×1
SparkFun MLX90614
×1
Texas Instruments LM386
×1
Speaker: 3W, 4 ohms
Speaker: 3W, 4 ohms
×1
RGB Backlight LCD - 16x2
Adafruit RGB Backlight LCD - 16x2
×1
Linear Regulator (7805)
Linear Regulator (7805)
×2
Bridge Rectifier Diode, Single Phase
Bridge Rectifier Diode, Single Phase
×1
BP Sensor
×1
DC POWER JACK 2.1MM BARREL-TYPE PCB MOUNT
TaydaElectronics DC POWER JACK 2.1MM BARREL-TYPE PCB MOUNT
×1
Arduino 101
Arduino 101
×1

Software apps and online services

Raspbian
Raspberry Pi Raspbian

Hand tools and fabrication machines

Soldering iron (generic)
Soldering iron (generic)
Solder Wire, Lead Free
Solder Wire, Lead Free
Drill / Driver, Cordless
Drill / Driver, Cordless
Multitool, Screwdriver
Multitool, Screwdriver

Story

Read more

Schematics

Project Schematics

Circuit Diagram of the project

Code

SpO2 Sensor Interfacing With Raspberry Pi

Python
Collecting The SpO2 Sensor data Through I2C, And displaying The Result On LCD
To Compile The Code You Need To add Additional Codes 1)hrcalc.py 2)hrdump.py 3)max30102.py
import RPi.GPIO as GPIO
import time
import board
import digitalio
import adafruit_character_lcd.character_lcd as characterlcd
import max30102
import hrcalc
switch = 16
GPIO.setmode(GPIO.BCM)
GPIO.setup(switch,GPIO.IN)
sensor = max30102.MAX30102()
lcd_columns = 16
lcd_rows = 2

lcd_rs = digitalio.DigitalInOut(board.D26)
lcd_en = digitalio.DigitalInOut(board.D19)
lcd_d7 = digitalio.DigitalInOut(board.D27)
lcd_d6 = digitalio.DigitalInOut(board.D22)
lcd_d5 = digitalio.DigitalInOut(board.D24)
lcd_d4 = digitalio.DigitalInOut(board.D25)
lcd_backlight = digitalio.DigitalInOut(board.D4)

# Initialise the lcd class
lcd = characterlcd.Character_LCD_Mono(
    lcd_rs, lcd_en, lcd_d4, lcd_d5, lcd_d6, lcd_d7, lcd_columns, lcd_rows, lcd_backlight
)
while True:
    
    if GPIO.input(switch) == 0:
        red, ir = sensor.read_sequential()
        hrcalc.calc_hr_and_spo2(ir,red)
    else:
        lcd.clear()
        lcd.cursor_position(0, 0)
        lcd.message ="Place the finger"
        lcd.cursor_position(0, 1)
        lcd.message ="on the sensor"
        time.sleep(0.5)

BP Sensor Interfacing Code

Python
BP Sensor Interfacing With Raspberry Pi and Classifying The Status of Results. Announcing The Status Over Voice And Displaying Results On LCD
import serial, time
import RPi.GPIO as GPIO
import re
import time
import board
import digitalio
import adafruit_character_lcd.character_lcd as characterlcd
import os
import time
from pygame import mixer
from gtts import gTTS
import os
from os import path
from pydub import AudioSegment

bp = 23# BP sensor Status 
lcd_columns = 16 #LCD Display Size
lcd_rows = 2

# Raspberry Pi Pin Config For Interfacing LCD:
lcd_rs = digitalio.DigitalInOut(board.D26)
lcd_en = digitalio.DigitalInOut(board.D19)
lcd_d7 = digitalio.DigitalInOut(board.D27)
lcd_d6 = digitalio.DigitalInOut(board.D22)
lcd_d5 = digitalio.DigitalInOut(board.D24)
lcd_d4 = digitalio.DigitalInOut(board.D25)
lcd_backlight = digitalio.DigitalInOut(board.D4)

# Initialise the lcd class
lcd = characterlcd.Character_LCD_Mono(
    lcd_rs, lcd_en, lcd_d4, lcd_d5, lcd_d6, lcd_d7, lcd_columns, lcd_rows, lcd_backlight
)

GPIO.setwarnings(False)
GPIO.setmode(GPIO.BCM)
GPIO.setup(bp,GPIO.IN)
ser = serial.Serial("/dev/ttyS0",9600)# The Bp sensor Baud Rate
ser.bytesize = serial.EIGHTBITS #number of bits per bytes
ser.parity = serial.PARITY_NONE #set parity check: no parity
ser.stopbits = serial.STOPBITS_ONE #number of stop bits
ser.timeout = 1            #non-block read
ser.xonxoff = False     #disable software flow control
ser.rtscts = False     #disable hardware (RTS/CTS) flow control
ser.dsrdtr = False       #disable hardware (DSR/DTR) flow control
file = open('BP_sensor_readings.txt', 'w') # creating A text file in folder
file.write(' Your Blood Pressure Details Are As Follows\n')
mixer.init()

while True:
    response = ser.readline().decode('ASCII')
    x = len(response)# checking the avalible bytes in the serial data
    if x >= 10:
        print("systolic,diastolic,Pulse")
        print(response)
        y = response.split(',') #spliting the each bytes with comma
        print(y)
        z = re.findall('[0-9]+', response) # extracting each byte
        print(z)
        a = [z] # creating extracted each byte in a array
        b = int(z[0]) # first byte in a array (Systollic)
        bb = z[0]
        c = int(z[1]) # second byte in a array (diastollic)
        cc = z[1]
        d = int(z[2])# third byte in a array (pulse)
        dd = z[2]
        print("Systolic :", b)
        print("Diastollic :",c)
        print("Pulse:" , d)
        lcd.cursor_position(0, 0)# coloumn,row
        lcd.message = "SYS:"
        lcd.cursor_position(4, 0)# coloumn,row
        lcd.message = bb
        lcd.cursor_position(8, 0)# coloumn,row
        lcd.message = "DIA:"
        lcd.cursor_position(12, 0)# coloumn,row
        lcd.message = cc
        lcd.cursor_position(0, 1)# coloumn,row
        lcd.message = "Pulse:"
        lcd.cursor_position(6, 1)# coloumn,row
        lcd.message = dd
        if b < 90 and c < 60:
            #print('Hypotension')
            file.write('Systollic:' + str(b) + ' . Diastolic:'+ str(c)+' . Pulse rate:' + str(d)+'. ,You have Hypotension')
            time.sleep(1)
            file.close()
            print('Hypotension')
        elif b > 120 and b < 139 and c > 80 and c < 90:
            #print('Prehypertension')
            file.write('Systolic:' + str(b) + ' . Diastolic:'+ str(c)+' . Pulse rate:' + str(d)+'. ,You have Prehypertension' + '\n')
            time.sleep(1)
            file.close()
            print('Prehypertension')
        elif b > 140 and b < 159 and c > 90 and c < 99:
            #print('stage 1 Hypertension')
            file.write('Systolic:' + str(b) + ' . Diastolic:'+ str(c)+' . Pulse rate:' + str(d)+'. ,You Have stage 1 Hypertension' + '\n')
            time.sleep(1)
            file.close()
            print('stage 1 Hypertension')
        elif b > 160 and b < 179 and c > 100 and c < 109:
            #print('stage 2 Hypertension')
            file.write('Systolic:' + str(b) + ' . Diastolic:'+ str(c)+' . Pulse rate:' + str(d)+'. ,You Have stage 2 Hypertension' + '\n')
            time.sleep(1)
            file.close()
            print('stage 2 Hypertension')
        elif b > 180 and c > 110:
            #print('Hypertensive Crisis')
            file.write('Systolic:' + str(b) + ' . Diastolic:'+ str(c)+' . Pulse rate:' + str(d)+'. ,You Have Hypertensive Crisis' + '\n')
            time.sleep(1)
            file.close()
            print('Hypertensive Crisis')
        else:
            file.write('Systolic:' + str(b) + ' . Diastolic:'+ str(c)+' . Pulse rate:' + str(d)+'. ,Normal Blood Pressure' + '\n')
            time.sleep(1)
            file.close()
            print('Normal_blood_pressure')
            
        print("file written")
        lcd.clear()
        lcd.cursor_position(0, 0)# coloumn,row
        lcd.message = "Details Saved In"
        lcd.cursor_position(0, 1)# coloumn,row
        lcd.message = "Text Format"
        f = open("/home/pi/BP_sensor_readings.txt", "r")# Opening File To read The data
        p = (f.read())
        print(p)#Display the data saved in the text file
        tts = gTTS(p, lang='en', tld='com.au')# GTTS Text Input For Converting Text To Voice
        tts.save('BP.mp3')#Saving The Voice file recieved from GTTS in MP3 Format
        input_file = "BP.mp3"# Converrting the MP3 FILE INTO .WAV Format
        output_file = "BP.wav"#Saving the WAV Converted File 
        sound = AudioSegment.from_mp3(input_file)
        sound.export(output_file, format="wav")
        sound = mixer.Sound("/home/pi/BP.wav")#Locating the Wav File 
        sound.play()#Play The audio
        time.sleep(5)
        

IR Temperature Sensor Interfacing With Raspberry Pi

Python
Collecting the Temperature sensor data through I2C and Displaying the Result On the LCD and data announcement OVer the Voice
import RPi.GPIO as GPIO
import time
import board
import digitalio
import adafruit_character_lcd.character_lcd as characterlcd
from smbus2 import SMBus
from mlx90614 import MLX90614
import os
import time
from pygame import mixer
from gtts import gTTS
import os
from os import path
from pydub import AudioSegment

lcd_columns = 16
lcd_rows = 2
irt = 6 # Proximity Sensor Pin
GPIO.setmode(GPIO.BCM)
GPIO.setup(irt,GPIO.IN)# Proximity Sensor Pin
bus = SMBus(1)
sensor = MLX90614(bus, address=0x5A)
lcd_rs = digitalio.DigitalInOut(board.D26)
lcd_en = digitalio.DigitalInOut(board.D19)
lcd_d7 = digitalio.DigitalInOut(board.D27)
lcd_d6 = digitalio.DigitalInOut(board.D22)
lcd_d5 = digitalio.DigitalInOut(board.D24)
lcd_d4 = digitalio.DigitalInOut(board.D25)
lcd_backlight = digitalio.DigitalInOut(board.D4)

# Initialise the lcd class
lcd = characterlcd.Character_LCD_Mono(
    lcd_rs, lcd_en, lcd_d4, lcd_d5, lcd_d6, lcd_d7, lcd_columns, lcd_rows, lcd_backlight
)

file = open('IRTEMP_readings.txt', 'w')
file.write(' Your BODY TEMPERATURE Details Are As Follows.\n')
mixer.init()

while True:   
    if GPIO.input(irt) == 1:
        print (sensor.get_obj_temp())
        temp_c = sensor.get_obj_temp()
        a = int(temp_c)
        lcd.clear()
        lcd.cursor_position(0, 0)# coloumn,row
        lcd.message = 'Temp = %.1f C' % temp_c
        file.write('TEMPERATURE:' + str(a) +'. Degree celcius')
        time.sleep(1)
        file.close()
        f = open("/home/pi/IRTEMP_readings.txt", "r")
        p = (f.read())
        print(p)
        tts = gTTS(p, lang='en', tld='com.au')
        tts.save('IR.mp3')
        input_file = "IR.mp3"
        output_file = "IR.wav"
        sound = AudioSegment.from_mp3(input_file)
        sound.export(output_file, format="wav")
        sound = mixer.Sound("/home/pi/IR.wav")
        sound.play()
        time.sleep(5)
    else:
        lcd.clear()
        lcd.cursor_position(0, 0)# coloumn,row
        lcd.message = "Place hand"
        lcd.cursor_position(0, 1)# coloumn,row
        lcd.message = "near the sensor"
        print("please place hand near the sensor")
    time.sleep(1)

hrcalc.py

Python
This Code needs to save in the exact location Of the SpO2 Sensor code folder
# -*-coding:utf-8

import numpy as np
import time
import board
import digitalio
import adafruit_character_lcd.character_lcd as characterlcd
# 25 samples per second (in algorithm.h)
SAMPLE_FREQ = 25
# taking moving average of 4 samples when calculating HR
# in algorithm.h, "DONOT CHANGE" comment is attached
MA_SIZE = 4
# sampling frequency * 4 (in algorithm.h)
BUFFER_SIZE = 100

lcd_columns = 16
lcd_rows = 2
lcd_rs = digitalio.DigitalInOut(board.D26)
lcd_en = digitalio.DigitalInOut(board.D19)
lcd_d7 = digitalio.DigitalInOut(board.D27)
lcd_d6 = digitalio.DigitalInOut(board.D22)
lcd_d5 = digitalio.DigitalInOut(board.D24)
lcd_d4 = digitalio.DigitalInOut(board.D25)
lcd_backlight = digitalio.DigitalInOut(board.D4)

# Initialise the lcd class
lcd = characterlcd.Character_LCD_Mono(
    lcd_rs, lcd_en, lcd_d4, lcd_d5, lcd_d6, lcd_d7, lcd_columns, lcd_rows, lcd_backlight
)

# this assumes ir_data and red_data as np.array
def calc_hr_and_spo2(ir_data, red_data):
    """
    By detecting  peaks of PPG cycle and corresponding AC/DC
    of red/infra-red signal, the an_ratio for the SPO2 is computed.
    """
    # get dc mean
    ir_mean = int(np.mean(ir_data))

    # remove DC mean and inver signal
    # this lets peak detecter detect valley
    x = -1 * (np.array(ir_data) - ir_mean)

    # 4 point moving average
    # x is np.array with int values, so automatically casted to int
    for i in range(x.shape[0] - MA_SIZE):
        x[i] = np.sum(x[i:i+MA_SIZE]) / MA_SIZE

    # calculate threshold
    n_th = int(np.mean(x))
    n_th = 30 if n_th < 30 else n_th  # min allowed
    n_th = 60 if n_th > 60 else n_th  # max allowed

    ir_valley_locs, n_peaks = find_peaks(x, BUFFER_SIZE, n_th, 4, 15)
    # print(ir_valley_locs[:n_peaks], ",", end="")
    peak_interval_sum = 0
    if n_peaks >= 4:
        #print("peaks:",n_peaks)
        for i in range(1, n_peaks):
            peak_interval_sum += (ir_valley_locs[i] - ir_valley_locs[i-1])
        peak_interval_sum = int(peak_interval_sum / (n_peaks - 1))
        hr = int(SAMPLE_FREQ * 60 / peak_interval_sum)
        hr_valid = True
    else:
        hr = 0  # unable to calculate because # of peaks are too small
        hr_valid = False
    if hr >= 45 and hr<=100:
        print("heart rate:",hr)
        lcd.clear()
        lcd.cursor_position(0, 1)# coloumn,row
        lcd.message = 'Heart rate : %.1f C' % hr
        time.sleep(0.5)
    

    # ---------spo2---------

    # find precise min near ir_valley_locs (???)
    exact_ir_valley_locs_count = n_peaks

    # find ir-red DC and ir-red AC for SPO2 calibration ratio
    # find AC/DC maximum of raw

    # FIXME: needed??
    for i in range(exact_ir_valley_locs_count):
        if ir_valley_locs[i] > BUFFER_SIZE:
            spo2 = -999  # do not use SPO2 since valley loc is out of range
            spo2_valid = False
            return hr, hr_valid, spo2, spo2_valid

    i_ratio_count = 0
    ratio = []

    # find max between two valley locations
    # and use ratio between AC component of Ir and Red DC component of Ir and Red for SpO2
    red_dc_max_index = -1
    ir_dc_max_index = -1
    for k in range(exact_ir_valley_locs_count-1):
        red_dc_max = -16777216
        ir_dc_max = -16777216
        if ir_valley_locs[k+1] - ir_valley_locs[k] > 3:
            for i in range(ir_valley_locs[k], ir_valley_locs[k+1]):
                if ir_data[i] > ir_dc_max:
                    ir_dc_max = ir_data[i]
                    ir_dc_max_index = i
                if red_data[i] > red_dc_max:
                    red_dc_max = red_data[i]
                    red_dc_max_index = i

            red_ac = int((red_data[ir_valley_locs[k+1]] - red_data[ir_valley_locs[k]]) * (red_dc_max_index - ir_valley_locs[k]))
            red_ac = red_data[ir_valley_locs[k]] + int(red_ac / (ir_valley_locs[k+1] - ir_valley_locs[k]))
            red_ac = red_data[red_dc_max_index] - red_ac  # subtract linear DC components from raw

            ir_ac = int((ir_data[ir_valley_locs[k+1]] - ir_data[ir_valley_locs[k]]) * (ir_dc_max_index - ir_valley_locs[k]))
            ir_ac = ir_data[ir_valley_locs[k]] + int(ir_ac / (ir_valley_locs[k+1] - ir_valley_locs[k]))
            ir_ac = ir_data[ir_dc_max_index] - ir_ac  # subtract linear DC components from raw

            nume = red_ac * ir_dc_max
            denom = ir_ac * red_dc_max
            if (denom > 0 and i_ratio_count < 5) and nume != 0:
                # original cpp implementation uses overflow intentionally.
                # but at 64-bit OS, Pyhthon 3.X uses 64-bit int and nume*100/denom does not trigger overflow
                # so using bit operation ( &0xffffffff ) is needed
                ratio.append(int(((nume * 100) & 0xffffffff) / denom))
                i_ratio_count += 1

    # choose median value since PPG signal may vary from beat to beat
    ratio = sorted(ratio)  # sort to ascending order
    mid_index = int(i_ratio_count / 2)

    ratio_ave = 0
    if mid_index > 1:
        ratio_ave = int((ratio[mid_index-1] + ratio[mid_index])/2)
    else:
        if len(ratio) != 0:
            ratio_ave = ratio[mid_index]

    # why 184?
    # print("ratio average: ", ratio_ave)
    if ratio_ave > 2 and ratio_ave < 184:
        # -45.060 * ratioAverage * ratioAverage / 10000 + 30.354 * ratioAverage / 100 + 94.845
        spo2 = -45.060 * (ratio_ave**2) / 10000.0 + 30.054 * ratio_ave / 100.0 + 94.845
        spo2_valid = True
    else:
        spo2 = 0
        spo2_valid = False

    if spo2 > 90:
        print("spo2:",spo2)
        lcd.clear()
        lcd.cursor_position(0, 0)# coloumn,row
        lcd.message = 'SPo2 = %.1f' % spo2
        time.sleep(0.5)

    return hr, hr_valid, spo2, spo2_valid
    return a


def find_peaks(x, size, min_height, min_dist, max_num):
    """
    Find at most MAX_NUM peaks above MIN_HEIGHT separated by at least MIN_DISTANCE
    """
    ir_valley_locs, n_peaks = find_peaks_above_min_height(x, size, min_height, max_num)
    ir_valley_locs, n_peaks = remove_close_peaks(n_peaks, ir_valley_locs, x, min_dist)

    n_peaks = min([n_peaks, max_num])

    return ir_valley_locs, n_peaks


def find_peaks_above_min_height(x, size, min_height, max_num):
    """
    Find all peaks above MIN_HEIGHT
    """

    i = 0
    n_peaks = 0
    ir_valley_locs = []  # [0 for i in range(max_num)]
    while i < size - 1:
        if x[i] > min_height and x[i] > x[i-1]:  # find the left edge of potential peaks
            n_width = 1
            # original condition i+n_width < size may cause IndexError
            # so I changed the condition to i+n_width < size - 1
            while i + n_width < size - 1 and x[i] == x[i+n_width]:  # find flat peaks
                n_width += 1
            if x[i] > x[i+n_width] and n_peaks < max_num:  # find the right edge of peaks
                # ir_valley_locs[n_peaks] = i
                ir_valley_locs.append(i)
                n_peaks += 1  # original uses post increment
                i += n_width + 1
            else:
                i += n_width
        else:
            i += 1

    return ir_valley_locs, n_peaks


def remove_close_peaks(n_peaks, ir_valley_locs, x, min_dist):
    """
    Remove peaks separated by less than MIN_DISTANCE
    """

    # should be equal to maxim_sort_indices_descend
    # order peaks from large to small
    # should ignore index:0
    sorted_indices = sorted(ir_valley_locs, key=lambda i: x[i])
    sorted_indices.reverse()

    # this "for" loop expression does not check finish condition
    # for i in range(-1, n_peaks):
    i = -1
    while i < n_peaks:
        old_n_peaks = n_peaks
        n_peaks = i + 1
        # this "for" loop expression does not check finish condition
        # for j in (i + 1, old_n_peaks):
        j = i + 1
        while j < old_n_peaks:
            n_dist = (sorted_indices[j] - sorted_indices[i]) if i != -1 else (sorted_indices[j] + 1)  # lag-zero peak of autocorr is at index -1
            if n_dist > min_dist or n_dist < -1 * min_dist:
                sorted_indices[n_peaks] = sorted_indices[j]
                n_peaks += 1  # original uses post increment
            j += 1
        i += 1

    sorted_indices[:n_peaks] = sorted(sorted_indices[:n_peaks])

    return sorted_indices, n_peaks

hrdump.py

Python
You need to store this file in exact same location SpO2 Interfacing Code. this code is not the main code
# -*-coding:utf-8-*-

import max30102

m = max30102.MAX30102()

red, ir = m.read_sequential(1000)

with open("./red.log", "w") as f:
    for r in red:
        f.write("{0}\n".format(r))
with open("./ir.log", "w") as f:
    for r in ir:
        f.write("{0}\n".format(r))

m.shutdown()

max30102,py

Python
This code needs to store in the exact same location As SpO2 Interfacing.py. this code contained the Max30102 Sensor Commands for Interfacing The Raspberry Pi
# -*-coding:utf-8-*-

# this code is currently for python 2.7
from __future__ import print_function
from time import sleep

import RPi.GPIO as GPIO
import smbus

# i2c address-es
# not required?
I2C_WRITE_ADDR = 0xAE
I2C_READ_ADDR = 0xAF

# register address-es
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
REG_MULTI_LED_CTRL1 = 0x11
REG_MULTI_LED_CTRL2 = 0x12

REG_TEMP_INTR = 0x1F
REG_TEMP_FRAC = 0x20
REG_TEMP_CONFIG = 0x21
REG_PROX_INT_THRESH = 0x30
REG_REV_ID = 0xFE
REG_PART_ID = 0xFF

# currently not used
MAX_BRIGHTNESS = 255


class MAX30102():
    # by default, this assumes that physical pin 7 (GPIO 4) is used as interrupt
    # by default, this assumes that the device is at 0x57 on channel 1
    def __init__(self, channel=1, address=0x57, gpio_pin=7):
        print("Sensor is connected successfully with Channel: {0}, address: 0x{1:x}".format(channel, address))
        self.address = address
        self.channel = channel
        self.bus = smbus.SMBus(self.channel)
        self.interrupt = gpio_pin

        # set gpio mode
        GPIO.setmode(GPIO.BCM)
        GPIO.setup(self.interrupt, GPIO.IN)

        self.reset()

        sleep(1)  # wait 1 sec

        # read & clear interrupt register (read 1 byte)
        reg_data = self.bus.read_i2c_block_data(self.address, REG_INTR_STATUS_1, 1)
        # print("[SETUP] reset complete with interrupt register0: {0}".format(reg_data))
        self.setup()
        print("[Configuration] setup complete")
        print("");
        print("         Place The Finger on The Sensor")

    def shutdown(self):
        """
        Shutdown the device.
        """
        self.bus.write_i2c_block_data(self.address, REG_MODE_CONFIG, [0x80])

    def reset(self):
        """
        Reset the device, this will clear all settings,
        so after running this, run setup() again.
        """
        self.bus.write_i2c_block_data(self.address, REG_MODE_CONFIG, [0x40])

    def setup(self, led_mode=0x03):
        """
        This will setup the device with the values written in sample Arduino code.
        """
        # INTR setting
        # 0xc0 : A_FULL_EN and PPG_RDY_EN = Interrupt will be triggered when
        # fifo almost full & new fifo data ready
        self.bus.write_i2c_block_data(self.address, REG_INTR_ENABLE_1, [0xc0])
        self.bus.write_i2c_block_data(self.address, REG_INTR_ENABLE_2, [0x00])

        # FIFO_WR_PTR[4:0]
        self.bus.write_i2c_block_data(self.address, REG_FIFO_WR_PTR, [0x00])
        # OVF_COUNTER[4:0]
        self.bus.write_i2c_block_data(self.address, REG_OVF_COUNTER, [0x00])
        # FIFO_RD_PTR[4:0]
        self.bus.write_i2c_block_data(self.address, REG_FIFO_RD_PTR, [0x00])

        # 0b 0100 1111
        # sample avg = 4, fifo rollover = false, fifo almost full = 17
        self.bus.write_i2c_block_data(self.address, REG_FIFO_CONFIG, [0x4f])

        # 0x02 for read-only, 0x03 for SpO2 mode, 0x07 multimode LED
        self.bus.write_i2c_block_data(self.address, REG_MODE_CONFIG, [led_mode])
        # 0b 0010 0111
        #SPO2_ADC range = 4096nA, SPO2 sample rate = 100Hz, LED pulse-width = 411uS
        self.bus.write_i2c_block_data(self.address, REG_SPO2_CONFIG, [0x27])

        # choose value for ~7mA for LED1
        self.bus.write_i2c_block_data(self.address, REG_LED1_PA, [0x24])
        # choose value for ~7mA for LED2
        self.bus.write_i2c_block_data(self.address, REG_LED2_PA, [0x24])
        # choose value fro ~25mA for Pilot LED
        self.bus.write_i2c_block_data(self.address, REG_PILOT_PA, [0x7f])

    # this won't validate the arguments!
    # use when changing the values from default
    def set_config(self, reg, value):
        self.bus.write_i2c_block_data(self.address, reg, value)

    def read_fifo(self):
        """
        This function will read the data register.
        """
        red_led = None
        ir_led = None

        # read 1 byte from registers (values are discarded)
        reg_INTR1 = self.bus.read_i2c_block_data(self.address, REG_INTR_STATUS_1, 1)
        reg_INTR2 = self.bus.read_i2c_block_data(self.address, REG_INTR_STATUS_2, 1)

        # read 6-byte data from the device
        d = self.bus.read_i2c_block_data(self.address, REG_FIFO_DATA, 6)

        # mask MSB [23:18]
        red_led = (d[0] << 16 | d[1] << 8 | d[2]) & 0x03FFFF
        ir_led = (d[3] << 16 | d[4] << 8 | d[5]) & 0x03FFFF
        #print("red led:",red_led)
        return red_led, ir_led

    def read_sequential(self, amount=100):
        """
        This function will read the red-led and ir-led `amount` times.
        This works as blocking function.
        """
        red_buf = []
        ir_buf = []
        for i in range(amount):
            while(GPIO.input(self.interrupt) == 1):
                # wait for interrupt signal, which means the data is available
                # do nothing here
                pass

            red, ir = self.read_fifo()

            red_buf.append(red)
            ir_buf.append(ir)

        return red_buf, ir_buf

Credits

vinay y.n

vinay y.n

26 projects • 45 followers
An electronic product engineer with 8 years of experience in the field. The passion for electronics began as a hobby 11 years ago.

Comments