pythonkivysensorskivy-languageheartbeat

Display heartbeat sensor's readings from python program in a Kivy label


I'm trying to display the values obtained from my heartbeat sensor in my Kivy App. I've tried using the data extraction method applied on the other sensors but it's not working for this sensor as it doesn't contain any functions.

I've tried multiple different approaches but they all haven't returned the desired output. Can someone please have a look at the sensor code and suggest some method to display the output in my kivy app.

Heartbeatsensot.py


import time
# Import the ADS1x15 module.
import Adafruit_ADS1x15


if __name__ == '__main__':

    adc = Adafruit_ADS1x15.ADS1015()
    # initialization 
    GAIN = 2/3  
    curState = 0
    thresh = 525  # mid point in the waveform
    P = 512
    T = 512
    stateChanged = 0
    sampleCounter = 0
    lastBeatTime = 0
    firstBeat = True
    secondBeat = False
    Pulse = False
    IBI = 600
    rate = [0]*10
    amp = 100

    lastTime = int(time.time()*1000)

    # Main loop. use Ctrl-c to stop the code
    while True:
        # read from the ADC
        Signal = adc.read_adc(0, gain=GAIN)   #TODO: Select the correct ADC channel. I have selected A0 here
        curTime = int(time.time()*1000)

        sampleCounter += curTime - lastTime;      #                   # keep track of the time in mS with this variable
        lastTime = curTime
        N = sampleCounter - lastBeatTime;     #  # monitor the time since the last beat to avoid noise
        #print N, Signal, curTime, sampleCounter, lastBeatTime

        ##  find the peak and trough of the pulse wave
        if Signal < thresh and N > (IBI/5.0)*3.0 :  #       # avoid dichrotic noise by waiting 3/5 of last IBI
            if Signal < T :                        # T is the trough
              T = Signal;                         # keep track of lowest point in pulse wave 

        if Signal > thresh and  Signal > P:           # thresh condition helps avoid noise
            P = Signal;                             # P is the peak
                                                # keep track of highest point in pulse wave

          #  NOW IT'S TIME TO LOOK FOR THE HEART BEAT
          # signal surges up in value every time there is a pulse
        if N > 250 :                                   # avoid high frequency noise
            if  (Signal > thresh) and  (Pulse == False) and  (N > (IBI/5.0)*3.0)  :       
              Pulse = True;                               # set the Pulse flag when we think there is a pulse
              IBI = sampleCounter - lastBeatTime;         # measure time between beats in mS
              lastBeatTime = sampleCounter;               # keep track of time for next pulse

              if secondBeat :                        # if this is the second beat, if secondBeat == TRUE
                secondBeat = False;                  # clear secondBeat flag
                for i in range(0,10):             # seed the running total to get a realisitic BPM at startup
                  rate[i] = IBI;                      

              if firstBeat :                        # if it's the first time we found a beat, if firstBeat == TRUE
                firstBeat = False;                   # clear firstBeat flag
                secondBeat = True;                   # set the second beat flag
                continue                              # IBI value is unreliable so discard it


              # keep a running total of the last 10 IBI values
              runningTotal = 0;                  # clear the runningTotal variable    

              for i in range(0,9):                # shift data in the rate array
                rate[i] = rate[i+1];                  # and drop the oldest IBI value 
                runningTotal += rate[i];              # add up the 9 oldest IBI values

              rate[9] = IBI;                          # add the latest IBI to the rate array
              runningTotal += rate[9];                # add the latest IBI to runningTotal
              runningTotal /= 10;                     # average the last 10 IBI values 
              BPM = 60000/runningTotal;               # how many beats can fit into a minute? that's BPM!
              print ('BPM: {}'.format(BPM))

        if Signal < thresh and Pulse == True :   # when the values are going down, the beat is over
            Pulse = False;                         # reset the Pulse flag so we can do it again
            amp = P - T;                           # get amplitude of the pulse wave
            thresh = amp/2 + T;                    # set thresh at 50% of the amplitude
            P = thresh;                            # reset these for next time
            T = thresh;

        if N > 2500 :                          # if 2.5 seconds go by without a beat
            thresh = 512;                          # set thresh default
            P = 512;                               # set P default
            T = 512;                               # set T default
            lastBeatTime = sampleCounter;          # bring the lastBeatTime up to date        
            firstBeat = True;                      # set these to avoid noise
            secondBeat = False;                    # when we get the heartbeat back
            print ("no beats found")

        time.sleep(0.005)

** the kivy app that I'm trying to add the readings to**

another.py file

from kivy.app import App
from kivy.uix.screenmanager import ScreenManager,Screen
from kivy.properties import ObjectProperty, StringProperty
from kivy.uix.boxlayout import BoxLayout
from kivy.clock import Clock
from kivy.uix.button import Button
from kivy.uix.label import Label
from kivy.lang import Builder






class MainScreen(Screen):
   pass

class ScreenThermo(Screen):

    def __init__(self,**kwargs):
        super(ScreenThermo, self).__init__(**kwargs)
        Clock.schedule_interval(self.displayHR, 10)


    def displayHR(self,dt):
        heartbeat = 'BPM: {}'.format(BPM)
        pulse = heartbeat
        self.manager.screen_thermo.ids.TempLabel.text = str(pulse)


    def on_enter(self, *args):
        self.__init__()
        pass

class ScreenManagement(ScreenManager):
    pass

presentation = Builder.load_file("heartk.kv")

class MenuApp(App):
    def build(self):
       return presentation 

if __name__ == '__main__':
    MenuApp().run()

.kv file

ScreenManagement:    
    id: screen_manager
    screen_thermo: screen_thermo
    MainScreen:
    ScreenThermo:
        id: screen_thermo
        name: 'thermo'
        manager: screen_manager

<MainScreen>:
    name: "main"
    Label:
        text: "Welcome"
        font_size: 60
        halign: 'center'
        valign: 'middle'
        pos_hint: {'x': .01, 'y': .05}
        on_touch_down: app.root.current = "thermo"



<ScreenThermo>:
    Label:
        text: " Pulse rate"
        font_size: 50
        pos: (35, 100)


    Label:
        id: TempLabel
        font_size: 60
        halign: 'center'
        valign: 'middle'


Solution

  • I think you can accomplish what you want with a few minor modifications. First add a StringProperty to your ScreenThermo class, and in the on_enter() method start a thread to run your Heartbeatsensot code as:

    from Heartbeatsensot import hearbeatsensot
    
    class ScreenThermo(Screen):
        BPM_string = StringProperty('BPM: Not Detected')
    
        def on_enter(self, *args):
            Thread(target=hearbeatsensot, args=(self,)).start()
    

    And in your kv file, add a reference to the new StringProperty:

    <ScreenThermo>:
        Label:
            text: " Pulse rate"
            font_size: 50
            pos: (35, 100)
    
    
        Label:
            id: TempLabel
            text: root.BPM_string  # references new StringProperty
            font_size: 60
            halign: 'center'
            valign: 'middle'
    

    Now you only need to put whatever you want to display in the TempLabel into the BPM_string property. To do that, change Heartbeatsensot.py to define a method that can be used in a Thread. Simply replace the if __name__ == '__main__': in that file to def hearbeatsensot(screenThermo): like this:

    import time
    # Import the ADS1x15 module.
    import Adafruit_ADS1x15
    
    
    def hearbeatsensot(screenThermo):
    
        adc = Adafruit_ADS1x15.ADS1015()
        # initialization
        GAIN = 2/3
        .
        .
        .
        screenThermo.BPM_string = 'BPM: 65'
        .
        .
        .
    

    Then, within that method just use somethink like screenThermo.BPM_string = 'BPM: 65' (or whatever you want to set it to). The reference to BPM_string in the kv file will automatically set up a binding to update the TempLabel whenever BPM_string is modified.