B4A Library Heart Rate Measurement - 100% embedded in B4A

The request came from here but making it open to anyone that wants to use it. It is for free...

Copy the attached jar's and xml's to your additional libs folder. Have used some old camera wrapper code that I have used before along with the AndroidPlot wrapper that I have also done long ago + some code from here to create a lib and B4A sample project that measures and displays heart rate using the back facing camera and flash/torch.

Not sure if the camera orientation is going to be correct on all devices but it is not important as the lib is really only interested in the change in RED when your finger covers the camera and the preview is active (and torch is on).


1.png
 

Attachments

  • HeartRateMonitorNew.xml
    13.5 KB · Views: 186
  • HeartRateMonitorNew.jar
    8.1 KB · Views: 178
  • AndroidPlot_0_6_2.xml
    233.8 KB · Views: 168
  • AndroidPlot_0_6_2.jar
    255.1 KB · Views: 178
  • b4aHeartRateMonitor.zip
    9.5 KB · Views: 202

DonManfred

Expert
Licensed User
I tried the Example project on my Device.
*** Service (starter) Create ***
** Service (starter) Start **
** Activity (main) Create, isFirst = true **
In getCameraInstance()
** Activity (main) Resume **
In onResume
In initCamera
beat = 58
** Activity (main) Pause, UserClosed = false **
In onPause
** Activity (main) Resume **
In onResume
In initCamera
main_activity_resume (java line: 458)
java.lang.NullPointerException: Attempt to invoke virtual method 'android.hardware.Camera$Parameters android.hardware.Camera.getParameters()' on a null object reference
at heartratemonitorwrapper.heartratemonitorWrapper.initCamera(heartratemonitorWrapper.java:212)
at heartratemonitorwrapper.heartratemonitorWrapper.onResumeInitializeResources(heartratemonitorWrapper.java:179)
at JHS.hrm.main._activity_resume(main.java:458)
at java.lang.reflect.Method.invoke(Native Method)
at anywheresoftware.b4a.BA.raiseEvent2(BA.java:196)
at anywheresoftware.b4a.BA.raiseEvent(BA.java:176)
at JHS.hrm.main$ResumeMessage.run(main.java:310)
at android.os.Handler.handleCallback(Handler.java:873)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:214)
at android.app.ActivityThread.main(ActivityThread.java:7073)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:494)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:965)

This is the second run. On the first it worked for a few beats and then it crashes with the same error.

There is no permission for Camera requested btw. The targetsdk should be 26 too
 

Johan Schoeman

Expert
Licensed User
I tried the Example project on my Device.


This is the second run. On the first it worked for a few beats and then it crashes with the same error.

There is no permission for Camera requested btw. The targetsdk should be 26 too
I am running it on a Samsung SM-G973F device with Android 9 and have not once had a crash - despite starting and stopping it numerous times.

I have left the Java source code in post #2 above for whoever wants to change it to their liking (including making it SDK 26+ compatible). The wrapper probably needs to be redone as it gets a camera instance in the initialize method of the wrapper.
 
Last edited:

Johan Schoeman

Expert
Licensed User
I know it is not related to B4A but have just tested this with Arduino and seems to be working fairly well....ran it via the Arduino IDE with the serial plotter and really gives a fair result as long as what you can keep your finger sufficiently still between the IR LED and the receiver while the loop is "looping"

 

Johan Schoeman

Expert
Licensed User
I tried the Example project on my Device.


This is the second run. On the first it worked for a few beats and then it crashes with the same error.

There is no permission for Camera requested btw. The targetsdk should be 26 too
Here you go - new library that I have used (actually, an old barcode scanner library that I have modified). Working with target SDK = 28 and Runtime Permissions.
B4A sample project attached
You need to extract the jar's and xml's for the 3 attached xxxLIB.zip files and copy the jar's and xml's to your additional library folder.

Sample code:
B4X:
#Region  Project Attributes
    #ApplicationLabel: b4aHeartRateMonitor_2
    #VersionCode: 1
    #VersionName:
    'SupportedOrientations possible values: unspecified, landscape or portrait.
    #SupportedOrientations: portrait
    #CanInstallToExternalStorage: False
#End Region


#Region  Activity Attributes
    #FullScreen: False
    #IncludeTitle: True
#End Region

Sub Process_Globals
    'These global variables will be declared once when the application starts.
    'These variables can be accessed from all modules.
    Dim nativeMe As JavaObject
    Private rp As RuntimePermissions
    Dim t As Timer

End Sub

Sub Globals
    'These global variables will be redeclared each time the activity is created.
    'These variables can only be accessed from this module.

    Private nqrcrv As HeartRateMonitor2
    
    Private Button1 As Button
    Private torchOn As Boolean = False
    
    Private Button2 As Button
    Private Button3 As Button
    
    Private lc1 As RealTimeLineChart
    Dim bufsize As Int = 100                                 'set the data buffer size here i.e how many values to display in the plot area
    Dim xlab(bufsize) As String
    
    Dim a As Int = 0
    Dim xlabeltrack As Int = 0
    Dim scanneractive As Boolean = False
    
End Sub

Sub Activity_Create(FirstTime As Boolean)
    'Do not forget to load the layout file created with the visual designer. For example:
    Activity.LoadLayout("main")
    
    lc1.Visible = False
    nqrcrv.Visible = False
    t.Initialize("t", 1000)
    
    nativeMe.InitializeContext

    nqrcrv.AutofocusInterval = 500

    lc1.GraphTitleColor = Colors.Magenta
    lc1.GraphTitleSkewX = -0.15
    lc1.GraphTitleBold = True
    lc1.GraphTitleTextSize = 15.0
    lc1.GraphPlotAreaBackgroundColor = Colors.Black          'this will paint the plotting area DrakGray regardless of what GraphBackgroundColor has been set to
    lc1.GraphBackgroundColor = Colors.Transparent               'this will paint everything within the outer frame to be white
    lc1.GraphFrameColor = Colors.Red                            'this adjusts only the outer frame color
    lc1.GraphFrameWidth = 2.0
    lc1.GraphBufferSize = bufsize
    lc1.GraphTitle = "Heart Rate Monitor"

    lc1.DomainLabelColor = Colors.Black
    lc1.DomainLabelTextSize = 25
    lc1.DomianLabel = "Time in Seconds"
    
    lc1.YaxisRangeMode = lc1.YaxisMode_FIXED               
    lc1.YaxisRange(40.0, 120.0)
    lc1.YaxisDivisions = 10
    lc1.YaxisLabelTicks = 1
    lc1.YaxisShowZero = True
    lc1.YaxisTitleTextSize = 30
    lc1.YaxisTitleColor = Colors.Black
    lc1.YaxisGridLineColor = Colors.Yellow
    lc1.YaxisLabelTextSize = 25
    lc1.YaxisLabelColor = Colors.Red
    lc1.YaxisLabelOrientation = 0
    lc1.YaxisTitle = "BPM"
    
    '************************ If you comment this code then the x-axis labels will be the index value of the buffer
    For i = 0 To bufsize - 1
        xlab(i) = "" & i
    Next
    lc1.XAxisLabels = xlab
    '*************************************************************************************************************
        
    lc1.XaxisLabelTextSize = 20
    lc1.XaxisLabelTextColor = Colors.Green
    lc1.XaxisGridLineColor = Colors.Yellow
    lc1.XaxisLabelOrientation = 0
    lc1.XaxisDivisions = 30
    lc1.XaxisLabelTicks = 1
    


    
    lc1.LegendTextSize = 15.0
    lc1.LegendTextColor = Colors.Magenta
    lc1.LegendBackgroundColor = Colors.Transparent
    
    'setup for Line 1
    lc1.Line_1_LineColor = Colors.Red
    lc1.Line_1_LineWidth = 2.0
    lc1.Line_1_DrawDash = False
    lc1.Line_1_LegendText = "Heart Rate"
    lc1.Line_1_DrawCubic = True

    lc1.NumberOfLineCharts = 1
    lc1.DrawTheGraphs

End Sub

Sub Activity_Resume

End Sub

Sub Activity_Pause (UserClosed As Boolean)
    
    If scanneractive = True Then                 'scanner active? If yes, stop it
        torchOn = False
        nqrcrv.stopCamera
        t.Enabled = False
        scanneractive = False
    End If   

End Sub


Sub Button1_Click
    
    
    If nqrcrv.Visible = True Then                'switch on/off the falsh/torch (camera must have been started for flash/torch to work)
        If torchOn = False Then
            nqrcrv.TorchEnabled = True
            torchOn = True
        Else
            nqrcrv.TorchEnabled = False
            torchOn = False
            
        End If   
    End If
    
End Sub

Sub Button2_Click

    Dim Result As Boolean = True
    If Not(rp.Check(rp.PERMISSION_CAMERA)) Then
        rp.CheckAndRequest(rp.PERMISSION_CAMERA)
        Wait For Activity_PermissionResult (Permission As String, Result As Boolean)
    End If
    If Result Then
        If scanneractive = False Then           'scanner active? If not, start scanner
            t.Enabled = True                    'start the timer           
            nqrcrv.Visible = True               'make the scanner visible
            lc1.Visible = True                  'make the line chart visible
            nqrcrv.setBackCamera                'use the back camera
            nqrcrv.AutofocusInterval = 300000
            nqrcrv.startCamera                  'start the camera to display the preview
            scanneractive = True                'scanner has been started
            lc1.DrawTheGraphs                   'tell graphing lib to start drawing when it gets data
            lc1.START                           'start the graph
        End If   
    End If
End Sub

Sub Button3_Click
    
    If scanneractive = True Then              'is scanner active?
        t.Enabled = False
        nqrcrv.Visible = False                'hide the scanner view when we stop
        nqrcrv.stopCamera                     'stop the camera
        lc1.Visible = False                   'hide the line chart
        scanneractive = False                 'set boolean variable to false - scanner is not active as we have stopped the scanner
        lc1.ClearAllData                       'clear the data from the graph
        a = 0                                  'set the counter (of the number of data points) to zero
    End If   
    
End Sub

Sub t_tick
    Dim hr As Int = 0
    hr = nqrcrv.HeartRate
    Log("Heart Rate = " & hr)
    lc1.addData(hr, 0, 0, 0, 0)     'get un updated heart rate every second
    nativeMe.RunMethod("playTone", Null)          'play a tone every tick of the timer
    a = a + 1
    If a = bufsize + 1 Then a = 0                 
  
    If xlabeltrack = bufsize + 1 Then
        xlabeltrack = 0
        lc1.ClearAllData                        'THIS WILL CLEAR THE DATA
        a = 0
        For i = 0 To bufsize - 1
            xlab(i) = "" & i
        Next
        lc1.XAxisLabels = xlab
        lc1.DrawTheGraphs
        lc1.START
    End If
 
    '************************ If you comment this code then the x-axis labels will be the index of the buffer
'    xlabeltrack = xlabeltrack + 1
'    If xlabeltrack > bufsize Then
'        xlab = shiftarray(xlab)
'        xlab(bufsize - 1) = "" & xlabeltrack
'        lc1.XAxisLabels = xlab
'    End If
'    xlab(bufsize - 1) = "" & xlabeltrack
    '**************************************************************************************************************
    
End Sub

Sub shiftarray (oldarray() As String) As String()
    
    Dim newarray(bufsize) As String
    For i = 0 To bufsize - 2
        newarray(i) = oldarray(i + 1)
    Next
    Return newarray
    
End Sub
    

#if Java

import android.media.ToneGenerator;
import android.media.AudioManager;

  public void playTone() {
      final ToneGenerator tg = new ToneGenerator(AudioManager.STREAM_NOTIFICATION, 100);
      tg.startTone(ToneGenerator.TONE_PROP_BEEP);
  }     

#End If

Manifest:
B4X:
'This code will be applied to the manifest file during compilation.
'You do not need to modify it in most cases.
'See this link for for more information: https://www.b4x.com/forum/showthread.php?p=78136
AddManifestText(
<uses-sdk android:minSdkVersion="5" android:targetSdkVersion="28"/>
<supports-screens android:largeScreens="true"
    android:normalScreens="true"
    android:smallScreens="true"
    android:anyDensity="true"/>)
SetApplicationAttribute(android:icon, "@drawable/icon")
SetApplicationAttribute(android:label, "$LABEL$")
'SetApplicationAttribute(android:theme, "@android:style/Theme.Holo")
AddPermission(android.permission.CAMERA)

AddManifestText(<uses-feature android:name="android.hardware.camera" android:required="true" />)
AddManifestText(<uses-feature android:name="android.hardware.camera.autofocus" android:required="true" />)
AddManifestText(<uses-feature android:name="android.hardware.camera.flash" android:required="true" />)

'End of default text.
 

Attachments

  • b4aHeartRateMonitor_2.zip
    10.6 KB · Views: 165
  • jhscoreLIB.zip
    450.8 KB · Views: 165
  • HeartRateMonitor2LIB.zip
    23.2 KB · Views: 154
  • AndroidPlot_0_6_2LIB.zip
    255.8 KB · Views: 171
Top