├── .gitattributes
├── .gitignore
├── Hardware
├── MAX30105-Sensor.lbr
├── MAX30105_Breakout.brd
├── MAX30105_Breakout.sch
└── MAX30105_Breakout_Schematic.pdf
├── HeartBeat-1.jpg
├── LICENSE.md
├── Libraries
└── Arduino
│ ├── .gitattributes
│ ├── .gitignore
│ ├── Examples
│ ├── Example1_Basic_Readings
│ │ └── Example1_Basic_Readings.ino
│ ├── Example2_Presence_Sensing
│ │ └── Example2_Presence_Sensing.ino
│ ├── Example3_Temperature_Sense
│ │ └── Example3_Temperature_Sense.ino
│ ├── Example4_HeartBeat_Plotter
│ │ └── Example4_HeartBeat_Plotter.ino
│ ├── Example5_HeartRate
│ │ ├── Example5_HeartRate.ino
│ │ └── License.ino
│ ├── Example6_FIFO_Readings
│ │ └── Example6_FIFO_Readings.ino
│ ├── Example7_Basic_Readings_Interrupts
│ │ └── Example7_Basic_Readings_Interrupts.ino
│ └── Example8_SPO2
│ │ ├── Example8_SPO2.ino
│ │ └── License.ino
│ ├── LICENSE.md
│ ├── README.md
│ ├── extras
│ └── HeartBeat.jpg
│ ├── keywords.txt
│ ├── library.properties
│ └── src
│ ├── MAX30105.cpp
│ ├── MAX30105.h
│ ├── heartRate.cpp
│ ├── heartRate.h
│ ├── spo2_algorithm.cpp
│ └── spo2_algorithm.h
├── MAX30105-Sensor-Layout-1.jpg
└── readme.md
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
4 | # Custom for Visual Studio
5 | *.cs diff=csharp
6 |
7 | # Standard to msysgit
8 | *.doc diff=astextplain
9 | *.DOC diff=astextplain
10 | *.docx diff=astextplain
11 | *.DOCX diff=astextplain
12 | *.dot diff=astextplain
13 | *.DOT diff=astextplain
14 | *.pdf diff=astextplain
15 | *.PDF diff=astextplain
16 | *.rtf diff=astextplain
17 | *.RTF diff=astextplain
18 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Windows image file caches
2 | Thumbs.db
3 | ehthumbs.db
4 |
5 | #Eagle Backup files
6 | *.s#?
7 | *.b#?
8 | *.l#?
9 | *.lck
10 |
11 | # Folder config file
12 | Desktop.ini
13 |
14 | # Recycle Bin used on file shares
15 | $RECYCLE.BIN/
16 |
17 | # Windows Installer files
18 | *.cab
19 | *.msi
20 | *.msm
21 | *.msp
22 |
23 | # =========================
24 | # Operating System Files
25 | # =========================
26 |
27 | # OSX
28 | # =========================
29 |
30 | .DS_Store
31 | .AppleDouble
32 | .LSOverride
33 |
34 | # Icon must ends with two \r.
35 | Icon
36 |
37 | # Thumbnails
38 | ._*
39 |
40 | # Files that might appear on external disk
41 | .Spotlight-V100
42 | .Trashes
43 |
--------------------------------------------------------------------------------
/Hardware/MAX30105-Sensor.lbr:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 | >Name
208 | >Value
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 | >NAME
232 | >VALUE
233 |
234 |
235 |
236 |
237 | <b>Description:</b> Particle Detection IC. Primarily used for smoke detection, can be used for Pulse Detection, Pulse Ox, and possible visual particle detection.
238 |
239 |
240 |
241 |
242 |
243 |
244 |
245 |
246 |
247 |
248 |
249 |
250 |
251 |
252 |
253 |
254 |
255 |
256 |
257 |
258 |
259 |
260 |
261 |
262 |
263 |
264 |
265 |
266 |
267 |
268 |
269 |
270 |
271 |
272 |
--------------------------------------------------------------------------------
/Hardware/MAX30105_Breakout_Schematic.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sparkfun/MAX30105_Particle_Sensor_Breakout/1c5c88c8b99ae6edd1ba226c16e37b433641d56e/Hardware/MAX30105_Breakout_Schematic.pdf
--------------------------------------------------------------------------------
/HeartBeat-1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sparkfun/MAX30105_Particle_Sensor_Breakout/1c5c88c8b99ae6edd1ba226c16e37b433641d56e/HeartBeat-1.jpg
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | SparkFun License Information
2 | ============================
3 |
4 | SparkFun uses two different licenses for our files — one for hardware and one for code.
5 |
6 | Hardware
7 | ---------
8 |
9 | **SparkFun hardware is released under [Creative Commons Share-alike 4.0 International](http://creativecommons.org/licenses/by-sa/4.0/).**
10 |
11 | Note: This is a human-readable summary of (and not a substitute for) the [license](http://creativecommons.org/licenses/by-sa/4.0/legalcode).
12 |
13 | You are free to:
14 |
15 | Share — copy and redistribute the material in any medium or format
16 | Adapt — remix, transform, and build upon the material
17 | for any purpose, even commercially.
18 | The licensor cannot revoke these freedoms as long as you follow the license terms.
19 | Under the following terms:
20 |
21 | Attribution — You must give appropriate credit, provide a link to the license, and indicate if changes were made. You may do so in any reasonable manner, but not in any way that suggests the licensor endorses you or your use.
22 | ShareAlike — If you remix, transform, or build upon the material, you must distribute your contributions under the same license as the original.
23 | No additional restrictions — You may not apply legal terms or technological measures that legally restrict others from doing anything the license permits.
24 | Notices:
25 |
26 | You do not have to comply with the license for elements of the material in the public domain or where your use is permitted by an applicable exception or limitation.
27 | No warranties are given. The license may not give you all of the permissions necessary for your intended use. For example, other rights such as publicity, privacy, or moral rights may limit how you use the material.
28 |
29 |
30 | Code
31 | --------
32 |
33 | **SparkFun code, firmware, and software is released under the MIT License(http://opensource.org/licenses/MIT).**
34 |
35 | The MIT License (MIT)
36 |
37 | Copyright (c) 2016 SparkFun Electronics
38 |
39 | Permission is hereby granted, free of charge, to any person obtaining a copy
40 | of this software and associated documentation files (the "Software"), to deal
41 | in the Software without restriction, including without limitation the rights
42 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
43 | copies of the Software, and to permit persons to whom the Software is
44 | furnished to do so, subject to the following conditions:
45 |
46 | The above copyright notice and this permission notice shall be included in all
47 | copies or substantial portions of the Software.
48 |
49 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
50 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
51 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
52 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
53 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
54 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
55 | SOFTWARE.
56 |
--------------------------------------------------------------------------------
/Libraries/Arduino/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
4 | # Custom for Visual Studio
5 | *.cs diff=csharp
6 |
7 | # Standard to msysgit
8 | *.doc diff=astextplain
9 | *.DOC diff=astextplain
10 | *.docx diff=astextplain
11 | *.DOCX diff=astextplain
12 | *.dot diff=astextplain
13 | *.DOT diff=astextplain
14 | *.pdf diff=astextplain
15 | *.PDF diff=astextplain
16 | *.rtf diff=astextplain
17 | *.RTF diff=astextplain
18 |
--------------------------------------------------------------------------------
/Libraries/Arduino/.gitignore:
--------------------------------------------------------------------------------
1 | #################
2 | ## SparkFun Useful stuff
3 | #################
4 |
5 | ## AVR Development
6 | *.eep
7 | *.elf
8 | *.lst
9 | *.lss
10 | *.sym
11 | *.d
12 | *.o
13 | *.srec
14 | *.map
15 |
16 | ## Notepad++ backup files
17 | *.bak
18 |
19 | ## BOM files
20 | *bom*
21 |
22 | #################
23 | ## Eclipse
24 | #################
25 |
26 | *.pydevproject
27 | .project
28 | .metadata
29 | bin/
30 | tmp/
31 | *.tmp
32 | *.bak
33 | *.swp
34 | *~.nib
35 | local.properties
36 | .classpath
37 | .settings/
38 | .loadpath
39 |
40 | # External tool builders
41 | .externalToolBuilders/
42 |
43 | # Locally stored "Eclipse launch configurations"
44 | *.launch
45 |
46 | # CDT-specific
47 | .cproject
48 |
49 | # PDT-specific
50 | .buildpath
51 |
52 |
53 | #############
54 | ## Eagle
55 | #############
56 |
57 | # Ignore the board and schematic backup files and lock files
58 | *.b#?
59 | *.s#?
60 | *.l#?
61 | *.lck
62 |
63 |
64 | #################
65 | ## Visual Studio
66 | #################
67 |
68 | ## Ignore Visual Studio temporary files, build results, and
69 | ## files generated by popular Visual Studio add-ons.
70 |
71 | # User-specific files
72 | *.suo
73 | *.user
74 | *.sln.docstates
75 |
76 | # Build results
77 | [Dd]ebug/
78 | [Rr]elease/
79 | *_i.c
80 | *_p.c
81 | *.ilk
82 | *.meta
83 | *.obj
84 | *.pch
85 | *.pdb
86 | *.pgc
87 | *.pgd
88 | *.rsp
89 | *.sbr
90 | *.tlb
91 | *.tli
92 | *.tlh
93 | *.tmp
94 | *.vspscc
95 | .builds
96 | *.dotCover
97 |
98 | ## TODO: If you have NuGet Package Restore enabled, uncomment this
99 | #packages/
100 |
101 | # Visual C++ cache files
102 | ipch/
103 | *.aps
104 | *.ncb
105 | *.opensdf
106 | *.sdf
107 |
108 | # Visual Studio profiler
109 | *.psess
110 | *.vsp
111 |
112 | # ReSharper is a .NET coding add-in
113 | _ReSharper*
114 |
115 | # Installshield output folder
116 | [Ee]xpress
117 |
118 | # DocProject is a documentation generator add-in
119 | DocProject/buildhelp/
120 | DocProject/Help/*.HxT
121 | DocProject/Help/*.HxC
122 | DocProject/Help/*.hhc
123 | DocProject/Help/*.hhk
124 | DocProject/Help/*.hhp
125 | DocProject/Help/Html2
126 | DocProject/Help/html
127 |
128 | # Click-Once directory
129 | publish
130 |
131 | # Others
132 | [Bb]in
133 | [Oo]bj
134 | sql
135 | TestResults
136 | *.Cache
137 | ClientBin
138 | stylecop.*
139 | ~$*
140 | *.dbmdl
141 | Generated_Code #added for RIA/Silverlight projects
142 |
143 | # Backup & report files from converting an old project file to a newer
144 | # Visual Studio version. Backup files are not needed, because we have git ;-)
145 | _UpgradeReport_Files/
146 | Backup*/
147 | UpgradeLog*.XML
148 |
149 |
150 | ############
151 | ## Windows
152 | ############
153 |
154 | # Windows image file caches
155 | Thumbs.db
156 |
157 | # Folder config file
158 | Desktop.ini
159 |
160 |
161 | #############
162 | ## Mac OS
163 | #############
164 |
165 | .DS_Store
166 |
167 |
168 | #############
169 | ## Linux
170 | #############
171 |
172 | # backup files (*.bak on Win)
173 | *~
174 |
175 |
176 | #############
177 | ## Python
178 | #############
179 |
180 | *.py[co]
181 |
182 | # Packages
183 | *.egg
184 | *.egg-info
185 | dist
186 | build
187 | eggs
188 | parts
189 | bin
190 | var
191 | sdist
192 | develop-eggs
193 | .installed.cfg
194 |
195 | # Installer logs
196 | pip-log.txt
197 |
198 | # Unit test / coverage reports
199 | .coverage
200 | .tox
201 |
202 | #Translations
203 | *.mo
204 |
205 | #Mr Developer
206 | .mr.developer.cfg
207 |
--------------------------------------------------------------------------------
/Libraries/Arduino/Examples/Example1_Basic_Readings/Example1_Basic_Readings.ino:
--------------------------------------------------------------------------------
1 | /*
2 | MAX30105 Breakout: Output all the raw Red/IR/Green readings
3 | By: Nathan Seidle @ SparkFun Electronics
4 | Date: October 2nd, 2016
5 | https://github.com/sparkfun/MAX30105_Breakout
6 |
7 | Outputs all Red/IR/Green values.
8 |
9 | Hardware Connections (Breakoutboard to Arduino):
10 | -5V = 5V (3.3V is allowed)
11 | -GND = GND
12 | -SDA = A4 (or SDA)
13 | -SCL = A5 (or SCL)
14 | -INT = Not connected
15 |
16 | The MAX30105 Breakout can handle 5V or 3.3V I2C logic. We recommend powering the board with 5V
17 | but it will also run at 3.3V.
18 |
19 | This code is released under the [MIT License](http://opensource.org/licenses/MIT).
20 | */
21 |
22 | #include
23 | #include "MAX30105.h"
24 |
25 | MAX30105 particleSensor;
26 |
27 | void setup()
28 | {
29 | Serial.begin(115200);
30 | Serial.println("Initializing...");
31 |
32 | // Initialize sensor
33 | if (!particleSensor.begin(Wire, I2C_SPEED_FAST)) //Use default I2C port, 400kHz speed
34 | {
35 | Serial.println("MAX30105 was not found. Please check wiring/power. ");
36 | while (1);
37 | }
38 |
39 | particleSensor.setup(); //Configure sensor. Use 6.4mA for LED drive
40 | }
41 |
42 | void loop()
43 | {
44 | Serial.print(" R[");
45 | Serial.print(particleSensor.getRed());
46 | Serial.print("] IR[");
47 | Serial.print(particleSensor.getIR());
48 | Serial.print("] G[");
49 | Serial.print(particleSensor.getGreen());
50 | Serial.print("]");
51 |
52 | Serial.println();
53 | }
54 |
--------------------------------------------------------------------------------
/Libraries/Arduino/Examples/Example2_Presence_Sensing/Example2_Presence_Sensing.ino:
--------------------------------------------------------------------------------
1 | /*
2 | MAX30105 Breakout: Take IR reading to sense presence
3 | By: Nathan Seidle @ SparkFun Electronics
4 | Date: October 2nd, 2016
5 | https://github.com/sparkfun/MAX30105_Breakout
6 |
7 | This takes an average reading at power up and if the reading changes more than 100
8 | then print 'Something is there!'.
9 |
10 | Hardware Connections (Breakoutboard to Arduino):
11 | -5V = 5V (3.3V is allowed)
12 | -GND = GND
13 | -SDA = A4 (or SDA)
14 | -SCL = A5 (or SCL)
15 | -INT = Not connected
16 |
17 | The MAX30105 Breakout can handle 5V or 3.3V I2C logic. We recommend powering the board with 5V
18 | but it will also run at 3.3V.
19 |
20 | */
21 |
22 | #include
23 | #include "MAX30105.h"
24 |
25 | MAX30105 particleSensor;
26 |
27 | long samplesTaken = 0; //Counter for calculating the Hz or read rate
28 | long unblockedValue; //Average IR at power up
29 | long startTime; //Used to calculate measurement rate
30 |
31 | void setup()
32 | {
33 | Serial.begin(115200);
34 | Serial.println("Initializing...");
35 |
36 | // Initialize sensor
37 | if (!particleSensor.begin(Wire, I2C_SPEED_FAST)) //Use default I2C port, 400kHz speed
38 | {
39 | Serial.println("MAX30105 was not found. Please check wiring/power. ");
40 | while (1);
41 | }
42 |
43 | //Setup to sense up to 18 inches, max LED brightness
44 | byte ledBrightness = 0xFF; //Options: 0=Off to 255=50mA
45 | byte sampleAverage = 4; //Options: 1, 2, 4, 8, 16, 32
46 | byte ledMode = 2; //Options: 1 = Red only, 2 = Red + IR, 3 = Red + IR + Green
47 | byte sampleRate = 400; //Options: 50, 100, 200, 400, 800, 1000, 1600, 3200
48 | int pulseWidth = 411; //Options: 69, 118, 215, 411
49 | int adcRange = 2048; //Options: 2048, 4096, 8192, 16384
50 |
51 | particleSensor.setup(ledBrightness, sampleAverage, ledMode, sampleRate, pulseWidth, adcRange); //Configure sensor with these settings
52 |
53 | particleSensor.setPulseAmplitudeRed(0); //Turn off Red LED
54 | particleSensor.setPulseAmplitudeGreen(0); //Turn off Green LED
55 |
56 | //Take an average of IR readings at power up
57 | unblockedValue = 0;
58 | for (byte x = 0 ; x < 32 ; x++)
59 | {
60 | unblockedValue += particleSensor.getIR(); //Read the IR value
61 | }
62 | unblockedValue /= 32;
63 |
64 | startTime = millis();
65 | }
66 |
67 | void loop()
68 | {
69 | samplesTaken++;
70 |
71 | Serial.print("IR[");
72 | Serial.print(particleSensor.getIR());
73 | Serial.print("] Hz[");
74 | Serial.print((float)samplesTaken / ((millis() - startTime) / 1000.0), 2);
75 | Serial.print("]");
76 |
77 | long currentDelta = particleSensor.getIR() - unblockedValue;
78 |
79 | Serial.print(" delta[");
80 | Serial.print(currentDelta);
81 | Serial.print("]");
82 |
83 | if (currentDelta > (long)100)
84 | {
85 | Serial.print(" Something is there!");
86 | }
87 |
88 | Serial.println();
89 | }
90 |
--------------------------------------------------------------------------------
/Libraries/Arduino/Examples/Example3_Temperature_Sense/Example3_Temperature_Sense.ino:
--------------------------------------------------------------------------------
1 | /*
2 | MAX3010 Breakout: Read the onboard temperature sensor
3 | By: Nathan Seidle @ SparkFun Electronics
4 | Date: October 20th, 2016
5 | https://github.com/sparkfun/MAX30105_Breakout
6 |
7 | This demo outputs the onboard temperature sensor. The temp sensor is accurate to +/-1 C but
8 | has an astonishing precision of 0.0625 C.
9 |
10 | Hardware Connections (Breakoutboard to Arduino):
11 | -5V = 5V (3.3V is allowed)
12 | -GND = GND
13 | -SDA = A4 (or SDA)
14 | -SCL = A5 (or SCL)
15 | -INT = Not connected
16 |
17 | The MAX30105 Breakout can handle 5V or 3.3V I2C logic. We recommend powering the board with 5V
18 | but it will also run at 3.3V.
19 | */
20 |
21 | #include
22 | #include "MAX30105.h"
23 |
24 | #include "heartRate.h"
25 |
26 | MAX30105 particleSensor;
27 |
28 | void setup()
29 | {
30 | Serial.begin(115200);
31 | Serial.println("Initializing...");
32 |
33 | // Initialize sensor
34 | if (!particleSensor.begin(Wire, I2C_SPEED_FAST)) //Use default I2C port, 400kHz speed
35 | {
36 | Serial.println("MAX30105 was not found. Please check wiring/power. ");
37 | while (1);
38 | }
39 |
40 | //The LEDs are very low power and won't affect the temp reading much but
41 | //you may want to turn off the LEDs to avoid any local heating
42 | particleSensor.setup(0); //Configure sensor. Turn off LEDs
43 | //particleSensor.setup(); //Configure sensor. Use 25mA for LED drive
44 | }
45 |
46 | void loop()
47 | {
48 | float temperature = particleSensor.readTemperature();
49 |
50 | Serial.print("temperatureC=");
51 | Serial.print(temperature, 4);
52 |
53 | float temperatureF = particleSensor.readTemperatureF(); //Because I am a bad global citizen
54 |
55 | Serial.print(" temperatureF=");
56 | Serial.print(temperatureF, 4);
57 |
58 | Serial.println();
59 | }
60 |
--------------------------------------------------------------------------------
/Libraries/Arduino/Examples/Example4_HeartBeat_Plotter/Example4_HeartBeat_Plotter.ino:
--------------------------------------------------------------------------------
1 | /*
2 | Heart beat plotting!
3 | By: Nathan Seidle @ SparkFun Electronics
4 | Date: October 20th, 2016
5 | https://github.com/sparkfun/MAX30105_Breakout
6 |
7 | Shows the user's heart beat on Arduino's serial plotter
8 |
9 | Instructions:
10 | 1) Load code onto Redboard
11 | 2) Attach sensor to your finger with a rubber band (see below)
12 | 3) Open Tools->'Serial Plotter'
13 | 4) Make sure the drop down is set to 115200 baud
14 | 5) Checkout the blips!
15 | 6) Feel the pulse on your neck and watch it mimic the blips
16 |
17 | It is best to attach the sensor to your finger using a rubber band or other tightening
18 | device. Humans are generally bad at applying constant pressure to a thing. When you
19 | press your finger against the sensor it varies enough to cause the blood in your
20 | finger to flow differently which causes the sensor readings to go wonky.
21 |
22 | Hardware Connections (Breakoutboard to Arduino):
23 | -5V = 5V (3.3V is allowed)
24 | -GND = GND
25 | -SDA = A4 (or SDA)
26 | -SCL = A5 (or SCL)
27 | -INT = Not connected
28 |
29 | The MAX30105 Breakout can handle 5V or 3.3V I2C logic. We recommend powering the board with 5V
30 | but it will also run at 3.3V.
31 | */
32 |
33 | #include
34 | #include "MAX30105.h"
35 |
36 | MAX30105 particleSensor;
37 |
38 | void setup()
39 | {
40 | Serial.begin(115200);
41 | Serial.println("Initializing...");
42 |
43 | // Initialize sensor
44 | if (!particleSensor.begin(Wire, I2C_SPEED_FAST)) //Use default I2C port, 400kHz speed
45 | {
46 | Serial.println("MAX30105 was not found. Please check wiring/power. ");
47 | while (1);
48 | }
49 |
50 | //Setup to sense a nice looking saw tooth on the plotter
51 | byte ledBrightness = 0x1F; //Options: 0=Off to 255=50mA
52 | byte sampleAverage = 8; //Options: 1, 2, 4, 8, 16, 32
53 | byte ledMode = 3; //Options: 1 = Red only, 2 = Red + IR, 3 = Red + IR + Green
54 | byte sampleRate = 100; //Options: 50, 100, 200, 400, 800, 1000, 1600, 3200
55 | int pulseWidth = 411; //Options: 69, 118, 215, 411
56 | int adcRange = 4096; //Options: 2048, 4096, 8192, 16384
57 |
58 | particleSensor.setup(ledBrightness, sampleAverage, ledMode, sampleRate, pulseWidth, adcRange); //Configure sensor with these settings
59 |
60 | //Arduino plotter auto-scales annoyingly. To get around this, pre-populate
61 | //the plotter with 500 of an average reading from the sensor
62 |
63 | //Take an average of IR readings at power up
64 | const byte avgAmount = 64;
65 | long baseValue = 0;
66 | for (byte x = 0 ; x < avgAmount ; x++)
67 | {
68 | baseValue += particleSensor.getIR(); //Read the IR value
69 | }
70 | baseValue /= avgAmount;
71 |
72 | //Pre-populate the plotter so that the Y scale is close to IR values
73 | for (int x = 0 ; x < 500 ; x++)
74 | Serial.println(baseValue);
75 | }
76 |
77 | void loop()
78 | {
79 | Serial.println(particleSensor.getIR()); //Send raw data to plotter
80 | }
81 |
--------------------------------------------------------------------------------
/Libraries/Arduino/Examples/Example5_HeartRate/Example5_HeartRate.ino:
--------------------------------------------------------------------------------
1 | /*
2 | Optical Heart Rate Detection (PBA Algorithm) using the MAX30105 Breakout
3 | By: Nathan Seidle @ SparkFun Electronics
4 | Date: October 2nd, 2016
5 | https://github.com/sparkfun/MAX30105_Breakout
6 |
7 | This is a demo to show the reading of heart rate or beats per minute (BPM) using
8 | a Penpheral Beat Amplitude (PBA) algorithm.
9 |
10 | It is best to attach the sensor to your finger using a rubber band or other tightening
11 | device. Humans are generally bad at applying constant pressure to a thing. When you
12 | press your finger against the sensor it varies enough to cause the blood in your
13 | finger to flow differently which causes the sensor readings to go wonky.
14 |
15 | Hardware Connections (Breakoutboard to Arduino):
16 | -5V = 5V (3.3V is allowed)
17 | -GND = GND
18 | -SDA = A4 (or SDA)
19 | -SCL = A5 (or SCL)
20 | -INT = Not connected
21 |
22 | The MAX30105 Breakout can handle 5V or 3.3V I2C logic. We recommend powering the board with 5V
23 | but it will also run at 3.3V.
24 | */
25 |
26 | #include
27 | #include "MAX30105.h"
28 |
29 | #include "heartRate.h"
30 |
31 | MAX30105 particleSensor;
32 |
33 | const byte RATE_SIZE = 4; //Increase this for more averaging. 4 is good.
34 | byte rates[RATE_SIZE]; //Array of heart rates
35 | byte rateSpot = 0;
36 | long lastBeat = 0; //Time at which the last beat occurred
37 |
38 | float beatsPerMinute;
39 | int beatAvg;
40 |
41 | void setup()
42 | {
43 | Serial.begin(115200);
44 | Serial.println("Initializing...");
45 |
46 | // Initialize sensor
47 | if (!particleSensor.begin(Wire, I2C_SPEED_FAST)) //Use default I2C port, 400kHz speed
48 | {
49 | Serial.println("MAX30105 was not found. Please check wiring/power. ");
50 | while (1);
51 | }
52 | Serial.println("Place your index finger on the sensor with steady pressure.");
53 |
54 | particleSensor.setup(); //Configure sensor with default settings
55 | particleSensor.setPulseAmplitudeRed(0x0A); //Turn Red LED to low to indicate sensor is running
56 | particleSensor.setPulseAmplitudeGreen(0); //Turn off Green LED
57 | }
58 |
59 | void loop()
60 | {
61 | long irValue = particleSensor.getIR();
62 |
63 | if (checkForBeat(irValue) == true)
64 | {
65 | //We sensed a beat!
66 | long delta = millis() - lastBeat;
67 | lastBeat = millis();
68 |
69 | beatsPerMinute = 60 / (delta / 1000.0);
70 |
71 | if (beatsPerMinute < 255 && beatsPerMinute > 20)
72 | {
73 | rates[rateSpot++] = (byte)beatsPerMinute; //Store this reading in the array
74 | rateSpot %= RATE_SIZE; //Wrap variable
75 |
76 | //Take average of readings
77 | beatAvg = 0;
78 | for (byte x = 0 ; x < RATE_SIZE ; x++)
79 | beatAvg += rates[x];
80 | beatAvg /= RATE_SIZE;
81 | }
82 | }
83 |
84 | Serial.print("IR=");
85 | Serial.print(irValue);
86 | Serial.print(", BPM=");
87 | Serial.print(beatsPerMinute);
88 | Serial.print(", Avg BPM=");
89 | Serial.print(beatAvg);
90 |
91 | if (irValue < 50000)
92 | Serial.print(" No finger?");
93 |
94 | Serial.println();
95 | }
96 |
97 |
98 |
--------------------------------------------------------------------------------
/Libraries/Arduino/Examples/Example5_HeartRate/License.ino:
--------------------------------------------------------------------------------
1 | /* Copyright (C) 2016 Maxim Integrated Products, Inc., All Rights Reserved.
2 | *
3 | * Permission is hereby granted, free of charge, to any person obtaining a
4 | * copy of this software and associated documentation files (the "Software"),
5 | * to deal in the Software without restriction, including without limitation
6 | * the rights to use, copy, modify, merge, publish, distribute, sublicense,
7 | * and/or sell copies of the Software, and to permit persons to whom the
8 | * Software is furnished to do so, subject to the following conditions:
9 | *
10 | * The above copyright notice and this permission notice shall be included
11 | * in all copies or substantial portions of the Software.
12 | *
13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
14 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
15 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
16 | * IN NO EVENT SHALL MAXIM INTEGRATED BE LIABLE FOR ANY CLAIM, DAMAGES
17 | * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
18 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
19 | * OTHER DEALINGS IN THE SOFTWARE.
20 | *
21 | * Except as contained in this notice, the name of Maxim Integrated
22 | * Products, Inc. shall not be used except as stated in the Maxim Integrated
23 | * Products, Inc. Branding Policy.
24 | *
25 | * The mere transfer of this software does not imply any licenses
26 | * of trade secrets, proprietary technology, copyrights, patents,
27 | * trademarks, maskwork rights, or any other form of intellectual
28 | * property whatsoever. Maxim Integrated Products, Inc. retains all
29 | * ownership rights.
30 | *
31 | */
32 |
--------------------------------------------------------------------------------
/Libraries/Arduino/Examples/Example6_FIFO_Readings/Example6_FIFO_Readings.ino:
--------------------------------------------------------------------------------
1 | /*
2 | MAX30105 Breakout: Take readings from the FIFO
3 | By: Nathan Seidle @ SparkFun Electronics
4 | Date: October 2nd, 2016
5 | https://github.com/sparkfun/MAX30105_Breakout
6 |
7 | Outputs all Red/IR/Green values at 25Hz by polling the FIFO
8 |
9 | Hardware Connections (Breakoutboard to Arduino):
10 | -5V = 5V (3.3V is allowed)
11 | -GND = GND
12 | -SDA = A4 (or SDA)
13 | -SCL = A5 (or SCL)
14 | -INT = Not connected
15 |
16 | The MAX30105 Breakout can handle 5V or 3.3V I2C logic. We recommend powering the board with 5V
17 | but it will also run at 3.3V.
18 |
19 | This code is released under the [MIT License](http://opensource.org/licenses/MIT).
20 | */
21 |
22 | #include
23 | #include "MAX30105.h"
24 |
25 | MAX30105 particleSensor;
26 |
27 | long startTime;
28 | long samplesTaken = 0; //Counter for calculating the Hz or read rate
29 |
30 | void setup()
31 | {
32 | Serial.begin(115200);
33 | Serial.println("Initializing...");
34 |
35 | // Initialize sensor
36 | if (!particleSensor.begin(Wire, I2C_SPEED_FAST)) //Use default I2C port, 400kHz speed
37 | {
38 | Serial.println("MAX30105 was not found. Please check wiring/power. ");
39 | while (1);
40 | }
41 |
42 | particleSensor.setup(); //Configure sensor. Use 6.4mA for LED drive
43 |
44 | startTime = millis();
45 | }
46 |
47 | void loop()
48 | {
49 | particleSensor.check(); //Check the sensor, read up to 3 samples
50 |
51 | while (particleSensor.available()) //do we have new data?
52 | {
53 | samplesTaken++;
54 |
55 | Serial.print(" R[");
56 | Serial.print(particleSensor.getFIFORed());
57 | Serial.print("] IR[");
58 | Serial.print(particleSensor.getFIFOIR());
59 | Serial.print("] G[");
60 | Serial.print(particleSensor.getFIFOGreen());
61 | Serial.print("] Hz[");
62 | Serial.print((float)samplesTaken / ((millis() - startTime) / 1000.0), 2);
63 | Serial.print("]");
64 |
65 | Serial.println();
66 |
67 | particleSensor.nextSample(); //We're finished with this sample so move to next sample
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/Libraries/Arduino/Examples/Example7_Basic_Readings_Interrupts/Example7_Basic_Readings_Interrupts.ino:
--------------------------------------------------------------------------------
1 | /*
2 | MAX30105 Breakout: Output all the raw Red/IR/Green readings, check INT pin and interrupt register
3 | By: Nathan Seidle @ SparkFun Electronics
4 | Date: October 2nd, 2016
5 | https://github.com/sparkfun/MAX30105_Breakout
6 |
7 | Outputs all Red/IR/Green values as fast as possible
8 | Checks the interrupt pin to see if an interrupt occurred
9 | Checks the interrupt register to see if a bit was set
10 |
11 | Hardware Connections (Breakoutboard to Arduino):
12 | -5V = 5V (3.3V is allowed)
13 | -GND = GND
14 | -SDA = A4 (or SDA)
15 | -SCL = A5 (or SCL)
16 | -INT = Not connected
17 |
18 | The MAX30105 Breakout can handle 5V or 3.3V I2C logic. We recommend powering the board with 5V
19 | but it will also run at 3.3V.
20 | */
21 |
22 | #include
23 | #include "MAX30105.h"
24 |
25 | MAX30105 particleSensor;
26 |
27 | long startTime;
28 | long samplesTaken = 0; //Counter for calculating the Hz or read rate
29 |
30 | byte interruptPin = 3; //Connect INT pin on breakout board to pin 3
31 |
32 | void setup()
33 | {
34 | pinMode(interruptPin, INPUT);
35 |
36 | Serial.begin(115200);
37 | Serial.println("Initializing...");
38 |
39 | // Initialize sensor
40 | if (!particleSensor.begin(Wire, I2C_SPEED_FAST)) //Use default I2C port, 400kHz speed
41 | {
42 | Serial.println("MAX30105 was not found. Please check wiring/power. ");
43 | while (1);
44 | }
45 |
46 | //Let's configure the sensor to run fast so we can over-run the buffer and cause an interrupt
47 | byte ledBrightness = 0x7F; //Options: 0=Off to 255=50mA
48 | byte sampleAverage = 1; //Options: 1, 2, 4, 8, 16, 32
49 | byte ledMode = 3; //Options: 1 = Red only, 2 = Red + IR, 3 = Red + IR + Green
50 | byte sampleRate = 400; //Options: 50, 100, 200, 400, 800, 1000, 1600, 3200
51 | int pulseWidth = 69; //Options: 69, 118, 215, 411
52 | int adcRange = 4096; //Options: 2048, 4096, 8192, 16384
53 |
54 | particleSensor.setup(ledBrightness, sampleAverage, ledMode, sampleRate, pulseWidth, adcRange); //Configure sensor with these settings
55 |
56 | particleSensor.enableAFULL(); //Enable the almost full interrupt (default is 32 samples)
57 |
58 | particleSensor.setFIFOAlmostFull(3); //Set almost full int to fire at 29 samples
59 |
60 | startTime = millis();
61 | }
62 |
63 | void loop()
64 | {
65 | particleSensor.check(); //Check the sensor, read up to 3 samples
66 |
67 | while (particleSensor.available()) //do we have new data?
68 | {
69 | samplesTaken++;
70 |
71 | Serial.print(" R[");
72 | Serial.print(particleSensor.getRed());
73 | Serial.print("] IR[");
74 | Serial.print(particleSensor.getIR());
75 | Serial.print("] G[");
76 | Serial.print(particleSensor.getGreen());
77 | Serial.print("] Hz[");
78 | Serial.print((float)samplesTaken / ((millis() - startTime) / 1000.0), 2);
79 | Serial.print("]");
80 |
81 | if (digitalRead(interruptPin) == LOW) //Hardware way of reading interrupts
82 | {
83 | Serial.print(" INT!");
84 | }
85 |
86 | byte flags = particleSensor.getINT1(); //Software way of reading interrupts
87 | if (flags)
88 | {
89 | Serial.print(" I[");
90 | Serial.print(flags, BIN);
91 | Serial.print("]");
92 | }
93 |
94 | Serial.println();
95 |
96 | particleSensor.nextSample(); //We're finished with this sample so move to next sample
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/Libraries/Arduino/Examples/Example8_SPO2/Example8_SPO2.ino:
--------------------------------------------------------------------------------
1 | /*
2 | Optical SP02 Detection (SPK Algorithm) using the MAX30105 Breakout
3 | By: Nathan Seidle @ SparkFun Electronics
4 | Date: October 19th, 2016
5 | https://github.com/sparkfun/MAX30105_Breakout
6 |
7 | This demo shows heart rate and SPO2 levels.
8 |
9 | It is best to attach the sensor to your finger using a rubber band or other tightening
10 | device. Humans are generally bad at applying constant pressure to a thing. When you
11 | press your finger against the sensor it varies enough to cause the blood in your
12 | finger to flow differently which causes the sensor readings to go wonky.
13 |
14 | This example is based on MAXREFDES117 and RD117_LILYPAD.ino from Maxim. Their example
15 | was modified to work with the SparkFun MAX30105 library and to compile under Arduino 1.6.11
16 | Please see license file for more info.
17 |
18 | Hardware Connections (Breakoutboard to Arduino):
19 | -5V = 5V (3.3V is allowed)
20 | -GND = GND
21 | -SDA = A4 (or SDA)
22 | -SCL = A5 (or SCL)
23 | -INT = Not connected
24 |
25 | The MAX30105 Breakout can handle 5V or 3.3V I2C logic. We recommend powering the board with 5V
26 | but it will also run at 3.3V.
27 | */
28 |
29 | #include
30 | #include "MAX30105.h"
31 | #include "spo2_algorithm.h"
32 |
33 | MAX30105 particleSensor;
34 |
35 | #define MAX_BRIGHTNESS 255
36 |
37 | //Arduino Uno doesn't have enough SRAM to store 100 samples of IR led data and red led data in 32-bit format
38 | //To solve this problem, 16-bit MSB of the sampled data will be truncated. Samples become 16-bit data.
39 | uint16_t irBuffer[100]; //infrared LED sensor data
40 | uint16_t redBuffer[100]; //red LED sensor data
41 |
42 | int32_t bufferLength; //data length
43 | int32_t spo2; //SPO2 value
44 | int8_t validSPO2; //indicator to show if the SPO2 calculation is valid
45 | int32_t heartRate; //heart rate value
46 | int8_t validHeartRate; //indicator to show if the heart rate calculation is valid
47 |
48 | byte pulseLED = 11; //Must be on PWM pin
49 | byte readLED = 13; //Blinks with each data read
50 |
51 | void setup()
52 | {
53 | Serial.begin(115200); // initialize serial communication at 115200 bits per second:
54 |
55 | pinMode(pulseLED, OUTPUT);
56 | pinMode(readLED, OUTPUT);
57 |
58 | // Initialize sensor
59 | if (!particleSensor.begin(Wire, I2C_SPEED_FAST)) //Use default I2C port, 400kHz speed
60 | {
61 | Serial.println(F("MAX30105 was not found. Please check wiring/power."));
62 | while (1);
63 | }
64 |
65 | Serial.println(F("Attach sensor to finger with rubber band. Press any key to start conversion"));
66 | while (Serial.available() == 0) ; //wait until user presses a key
67 | Serial.read();
68 |
69 | byte ledBrightness = 60; //Options: 0=Off to 255=50mA
70 | byte sampleAverage = 4; //Options: 1, 2, 4, 8, 16, 32
71 | byte ledMode = 2; //Options: 1 = Red only, 2 = Red + IR, 3 = Red + IR + Green
72 | byte sampleRate = 100; //Options: 50, 100, 200, 400, 800, 1000, 1600, 3200
73 | int pulseWidth = 411; //Options: 69, 118, 215, 411
74 | int adcRange = 4096; //Options: 2048, 4096, 8192, 16384
75 |
76 | particleSensor.setup(ledBrightness, sampleAverage, ledMode, sampleRate, pulseWidth, adcRange); //Configure sensor with these settings
77 | }
78 |
79 | void loop()
80 | {
81 | bufferLength = 100; //buffer length of 100 stores 4 seconds of samples running at 25sps
82 |
83 | //read the first 100 samples, and determine the signal range
84 | for (byte i = 0 ; i < bufferLength ; i++)
85 | {
86 | while (particleSensor.available() == false) //do we have new data?
87 | particleSensor.check(); //Check the sensor for new data
88 |
89 | redBuffer[i] = particleSensor.getRed();
90 | irBuffer[i] = particleSensor.getIR();
91 | particleSensor.nextSample(); //We're finished with this sample so move to next sample
92 |
93 | Serial.print(F("red="));
94 | Serial.print(redBuffer[i], DEC);
95 | Serial.print(F(", ir="));
96 | Serial.println(irBuffer[i], DEC);
97 | }
98 |
99 | //calculate heart rate and SpO2 after first 100 samples (first 4 seconds of samples)
100 | maxim_heart_rate_and_oxygen_saturation(irBuffer, bufferLength, redBuffer, &spo2, &validSPO2, &heartRate, &validHeartRate);
101 |
102 | //Continuously taking samples from MAX30102. Heart rate and SpO2 are calculated every 1 second
103 | while (1)
104 | {
105 | //dumping the first 25 sets of samples in the memory and shift the last 75 sets of samples to the top
106 | for (byte i = 25; i < 100; i++)
107 | {
108 | redBuffer[i - 25] = redBuffer[i];
109 | irBuffer[i - 25] = irBuffer[i];
110 | }
111 |
112 | //take 25 sets of samples before calculating the heart rate.
113 | for (byte i = 75; i < 100; i++)
114 | {
115 | while (particleSensor.available() == false) //do we have new data?
116 | particleSensor.check(); //Check the sensor for new data
117 |
118 | digitalWrite(readLED, !digitalRead(readLED)); //Blink onboard LED with every data read
119 |
120 | redBuffer[i] = particleSensor.getRed();
121 | irBuffer[i] = particleSensor.getIR();
122 | particleSensor.nextSample(); //We're finished with this sample so move to next sample
123 |
124 | //send samples and calculation result to terminal program through UART
125 | Serial.print(F("red="));
126 | Serial.print(redBuffer[i], DEC);
127 | Serial.print(F(", ir="));
128 | Serial.print(irBuffer[i], DEC);
129 |
130 | Serial.print(F(", HR="));
131 | Serial.print(heartRate, DEC);
132 |
133 | Serial.print(F(", HRvalid="));
134 | Serial.print(validHeartRate, DEC);
135 |
136 | Serial.print(F(", SPO2="));
137 | Serial.print(spo2, DEC);
138 |
139 | Serial.print(F(", SPO2Valid="));
140 | Serial.println(validSPO2, DEC);
141 | }
142 |
143 | //After gathering 25 new samples recalculate HR and SP02
144 | maxim_heart_rate_and_oxygen_saturation(irBuffer, bufferLength, redBuffer, &spo2, &validSPO2, &heartRate, &validHeartRate);
145 | }
146 | }
147 |
148 |
--------------------------------------------------------------------------------
/Libraries/Arduino/Examples/Example8_SPO2/License.ino:
--------------------------------------------------------------------------------
1 | /* Copyright (C) 2016 Maxim Integrated Products, Inc., All Rights Reserved.
2 | *
3 | * Permission is hereby granted, free of charge, to any person obtaining a
4 | * copy of this software and associated documentation files (the "Software"),
5 | * to deal in the Software without restriction, including without limitation
6 | * the rights to use, copy, modify, merge, publish, distribute, sublicense,
7 | * and/or sell copies of the Software, and to permit persons to whom the
8 | * Software is furnished to do so, subject to the following conditions:
9 | *
10 | * The above copyright notice and this permission notice shall be included
11 | * in all copies or substantial portions of the Software.
12 | *
13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
14 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
15 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
16 | * IN NO EVENT SHALL MAXIM INTEGRATED BE LIABLE FOR ANY CLAIM, DAMAGES
17 | * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
18 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
19 | * OTHER DEALINGS IN THE SOFTWARE.
20 | *
21 | * Except as contained in this notice, the name of Maxim Integrated
22 | * Products, Inc. shall not be used except as stated in the Maxim Integrated
23 | * Products, Inc. Branding Policy.
24 | *
25 | * The mere transfer of this software does not imply any licenses
26 | * of trade secrets, proprietary technology, copyrights, patents,
27 | * trademarks, maskwork rights, or any other form of intellectual
28 | * property whatsoever. Maxim Integrated Products, Inc. retains all
29 | * ownership rights.
30 | *
31 | */
32 |
--------------------------------------------------------------------------------
/Libraries/Arduino/LICENSE.md:
--------------------------------------------------------------------------------
1 | SparkFun License Information
2 | ============================
3 |
4 | SparkFun uses two different licenses for our files — one for hardware and one for code.
5 |
6 | Hardware
7 | ---------
8 |
9 | **SparkFun hardware is released under [Creative Commons Share-alike 4.0 International](http://creativecommons.org/licenses/by-sa/4.0/).**
10 |
11 | Note: This is a human-readable summary of (and not a substitute for) the [license](http://creativecommons.org/licenses/by-sa/4.0/legalcode).
12 |
13 | You are free to:
14 |
15 | Share — copy and redistribute the material in any medium or format
16 | Adapt — remix, transform, and build upon the material
17 | for any purpose, even commercially.
18 | The licensor cannot revoke these freedoms as long as you follow the license terms.
19 | Under the following terms:
20 |
21 | Attribution — You must give appropriate credit, provide a link to the license, and indicate if changes were made. You may do so in any reasonable manner, but not in any way that suggests the licensor endorses you or your use.
22 | ShareAlike — If you remix, transform, or build upon the material, you must distribute your contributions under the same license as the original.
23 | No additional restrictions — You may not apply legal terms or technological measures that legally restrict others from doing anything the license permits.
24 | Notices:
25 |
26 | You do not have to comply with the license for elements of the material in the public domain or where your use is permitted by an applicable exception or limitation.
27 | No warranties are given. The license may not give you all of the permissions necessary for your intended use. For example, other rights such as publicity, privacy, or moral rights may limit how you use the material.
28 |
29 |
30 | Code
31 | --------
32 |
33 | **SparkFun code, firmware, and software is released under the MIT License(http://opensource.org/licenses/MIT).**
34 |
35 | The MIT License (MIT)
36 |
37 | Copyright (c) 2016 SparkFun Electronics
38 |
39 | Permission is hereby granted, free of charge, to any person obtaining a copy
40 | of this software and associated documentation files (the "Software"), to deal
41 | in the Software without restriction, including without limitation the rights
42 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
43 | copies of the Software, and to permit persons to whom the Software is
44 | furnished to do so, subject to the following conditions:
45 |
46 | The above copyright notice and this permission notice shall be included in all
47 | copies or substantial portions of the Software.
48 |
49 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
50 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
51 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
52 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
53 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
54 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
55 | SOFTWARE.
56 |
--------------------------------------------------------------------------------
/Libraries/Arduino/README.md:
--------------------------------------------------------------------------------
1 | SparkFun MAX301x Particle Sensor Library
2 | ===========================================================
3 |
4 | ](https://cdn.sparkfun.com/assets/parts/1/1/8/7/4/14045-02.jpg)
5 |
6 | [*SparkFun Reflectance, Particle, and Pulse Ox sensor - MAX30105 (SEN-14045)*](https://www.sparkfun.com/products/14045)
7 |
8 | 
9 |
10 | [*SparkFun Reflectance, Particle, and Pulse Ox sensor - MAX30105 (SEN-14045)*](https://www.sparkfun.com/products/14045)
11 |
12 | This is a breadboard friendly breakout board for the Maxim MAX30105 reflectance, particle, and pulse ox sensor.
13 |
14 | This library should work with other MAX3010x sensors including the MAX30102, MAX30101, and MAX30100.
15 |
16 | Library written by Nathan Seidle ([SparkFun](http://www.sparkfun.com)) and Peter Jansen ([Open Sensing Lab](https://github.com/opensensinglab)).
17 |
18 | Repository Contents
19 | -------------------
20 |
21 | * **/examples** - Example sketches for the library (.ino). Run these from the Arduino IDE.
22 | * **/src** - Source files for the library (.cpp, .h).
23 | * **keywords.txt** - Keywords from this library that will be highlighted in the Arduino IDE.
24 | * **library.properties** - General library properties for the Arduino package manager.
25 |
26 | Documentation
27 | --------------
28 |
29 | * **[Installing an Arduino Library Guide](https://learn.sparkfun.com/tutorials/installing-an-arduino-library)** - Basic information on how to install an Arduino library.
30 | * **[Product Repository](https://github.com/sparkfun/HTU21D_Breakout)** - Main repository (including hardware files) for the HTU21D breakout board.
31 | * **[Hookup Guide](https://learn.sparkfun.com/tutorials/max30105-particle-and-pulse-ox-sensor-hookup-guide)** - Basic hookup guide for the MAX30105 Particle Sensor breakout board.
32 |
33 | License Information
34 | -------------------
35 |
36 | This product is _**open source**_!
37 |
38 | Various bits of the code have different licenses applied. Anything SparkFun wrote is beerware; if you see me (or any other SparkFun employee) at the local, and you've found our code helpful, please buy us a round! Anything Maxim wrote has its own license. Anything that was co-writing with Peter Jansen is BSD.
39 |
40 | Please use, reuse, and modify these files as you see fit. Please maintain attribution to SparkFun Electronics and release anything derivative under the same license.
41 |
42 | Distributed as-is; no warranty is given.
43 |
44 | - Your friends at SparkFun.
45 |
--------------------------------------------------------------------------------
/Libraries/Arduino/extras/HeartBeat.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sparkfun/MAX30105_Particle_Sensor_Breakout/1c5c88c8b99ae6edd1ba226c16e37b433641d56e/Libraries/Arduino/extras/HeartBeat.jpg
--------------------------------------------------------------------------------
/Libraries/Arduino/keywords.txt:
--------------------------------------------------------------------------------
1 | #######################################
2 | # Syntax Coloring Map
3 | #######################################
4 |
5 | #######################################
6 | # Datatypes (KEYWORD1)
7 | #######################################
8 |
9 | MAX30105 KEYWORD1
10 |
11 | #######################################
12 | # Methods and Functions (KEYWORD2)
13 | #######################################
14 |
15 | begin KEYWORD2
16 | setup KEYWORD2
17 | available KEYWORD2
18 | getRed KEYWORD2
19 | getIR KEYWORD2
20 | getGreen KEYWORD2
21 | readTemperature KEYWORD2
22 | readTemperatureF KEYWORD2
23 |
24 | check KEYWORD2
25 | getRed KEYWORD2
26 | getIR KEYWORD2
27 | getGreen KEYWORD2
28 |
29 | softReset KEYWORD2
30 | shutDown KEYWORD2
31 | wakeUp KEYWORD2
32 | setLEDMode KEYWORD2
33 | setADCRange KEYWORD2
34 | setSampleRate KEYWORD2
35 | setPulseWidth KEYWORD2
36 | setPulseAmplitudeRed KEYWORD2
37 | setPulseAmplitudeIR KEYWORD2
38 | setPulseAmplitudeGreen KEYWORD2
39 | setPulseAmplitudeProximity KEYWORD2
40 | setProximityThreshold KEYWORD2
41 | enableSlot KEYWORD2
42 | disableSlots KEYWORD2
43 | getINT1 KEYWORD2
44 | getINT2 KEYWORD2
45 | enableAFULL KEYWORD2
46 | disableAFULL KEYWORD2
47 | enableDATARDY KEYWORD2
48 | disableDATARDY KEYWORD2
49 | enableALCOVF KEYWORD2
50 | disableALCOVF KEYWORD2
51 | enablePROXINT KEYWORD2
52 | disablePROXINT KEYWORD2
53 | enableDIETEMPRDY KEYWORD2
54 | disableDIETEMPRDY KEYWORD2
55 |
56 | setFIFOAverage KEYWORD2
57 | enableFIFORollover KEYWORD2
58 | disableFIFORollover KEYWORD2
59 | setFIFOAlmostFull KEYWORD2
60 |
61 | getFIFORed KEYWORD2
62 | getFIFOIR KEYWORD2
63 | getFIFOGreen KEYWORD2
64 | getWritePointer KEYWORD2
65 | getReadPointer KEYWORD2
66 | clearFIFO KEYWORD2
67 | available KEYWORD2
68 |
69 | nextSample KEYWORD2
70 |
71 | setPROXINTTHRESH KEYWORD2
72 |
73 | getRevisionID KEYWORD2
74 | readPartID KEYWORD2
75 |
76 | readRegister8 KEYWORD2
77 | writeRegister8 KEYWORD2
78 |
79 | #######################################
80 | # Constants (LITERAL1)
81 | #######################################
--------------------------------------------------------------------------------
/Libraries/Arduino/library.properties:
--------------------------------------------------------------------------------
1 | name=SparkFun MAX3010x Pulse and Proximity Sensor Library
2 | version=1.0.0
3 | author=SparkFun Electronics
4 | maintainer=SparkFun Electronics
5 | sentence=Library for the MAX30102 Pulse and MAX30105 Proximity Breakout
6 | paragraph=An Arduino Library for the MAX3015 particle sensor and MAX30102 Pulse Ox sensor
7 | category=Sensors
8 | url=https://github.com/sparkfun/SparkFun_MAX3010x_Sensor_Library
9 | architectures=*
10 |
--------------------------------------------------------------------------------
/Libraries/Arduino/src/MAX30105.cpp:
--------------------------------------------------------------------------------
1 | /***************************************************
2 | This is a library written for the Maxim MAX30105 Optical Smoke Detector
3 | It should also work with the MAX30102. However, the MAX30102 does not have a Green LED.
4 |
5 | These sensors use I2C to communicate, as well as a single (optional)
6 | interrupt line that is not currently supported in this driver.
7 |
8 | Written by Peter Jansen and Nathan Seidle (SparkFun)
9 | BSD license, all text above must be included in any redistribution.
10 | *****************************************************/
11 |
12 | #include "MAX30105.h"
13 |
14 | // Status Registers
15 | static const uint8_t MAX30105_INTSTAT1 = 0x00;
16 | static const uint8_t MAX30105_INTSTAT2 = 0x01;
17 | static const uint8_t MAX30105_INTENABLE1 = 0x02;
18 | static const uint8_t MAX30105_INTENABLE2 = 0x03;
19 |
20 | // FIFO Registers
21 | static const uint8_t MAX30105_FIFOWRITEPTR = 0x04;
22 | static const uint8_t MAX30105_FIFOOVERFLOW = 0x05;
23 | static const uint8_t MAX30105_FIFOREADPTR = 0x06;
24 | static const uint8_t MAX30105_FIFODATA = 0x07;
25 |
26 | // Configuration Registers
27 | static const uint8_t MAX30105_FIFOCONFIG = 0x08;
28 | static const uint8_t MAX30105_MODECONFIG = 0x09;
29 | static const uint8_t MAX30105_PARTICLECONFIG = 0x0A; // Note, sometimes listed as "SPO2" config in datasheet (pg. 11)
30 | static const uint8_t MAX30105_LED1_PULSEAMP = 0x0C;
31 | static const uint8_t MAX30105_LED2_PULSEAMP = 0x0D;
32 | static const uint8_t MAX30105_LED3_PULSEAMP = 0x0E;
33 | static const uint8_t MAX30105_LED_PROX_AMP = 0x10;
34 | static const uint8_t MAX30105_MULTILEDCONFIG1 = 0x11;
35 | static const uint8_t MAX30105_MULTILEDCONFIG2 = 0x12;
36 |
37 | // Die Temperature Registers
38 | static const uint8_t MAX30105_DIETEMPINT = 0x1F;
39 | static const uint8_t MAX30105_DIETEMPFRAC = 0x20;
40 | static const uint8_t MAX30105_DIETEMPCONFIG = 0x21;
41 |
42 | // Proximity Function Registers
43 | static const uint8_t MAX30105_PROXINTTHRESH = 0x30;
44 |
45 | // Part ID Registers
46 | static const uint8_t MAX30105_REVISIONID = 0xFE;
47 | static const uint8_t MAX30105_PARTID = 0xFF; // Should always be 0x15. Identical to MAX30102.
48 |
49 | // MAX30105 Commands
50 | // Interrupt configuration (pg 13, 14)
51 | static const uint8_t MAX30105_INT_A_FULL_MASK = (byte)~0b10000000;
52 | static const uint8_t MAX30105_INT_A_FULL_ENABLE = 0x80;
53 | static const uint8_t MAX30105_INT_A_FULL_DISABLE = 0x00;
54 |
55 | static const uint8_t MAX30105_INT_DATA_RDY_MASK = (byte)~0b01000000;
56 | static const uint8_t MAX30105_INT_DATA_RDY_ENABLE = 0x40;
57 | static const uint8_t MAX30105_INT_DATA_RDY_DISABLE = 0x00;
58 |
59 | static const uint8_t MAX30105_INT_ALC_OVF_MASK = (byte)~0b00100000;
60 | static const uint8_t MAX30105_INT_ALC_OVF_ENABLE = 0x20;
61 | static const uint8_t MAX30105_INT_ALC_OVF_DISABLE = 0x00;
62 |
63 | static const uint8_t MAX30105_INT_PROX_INT_MASK = (byte)~0b00010000;
64 | static const uint8_t MAX30105_INT_PROX_INT_ENABLE = 0x10;
65 | static const uint8_t MAX30105_INT_PROX_INT_DISABLE = 0x00;
66 |
67 | static const uint8_t MAX30105_INT_DIE_TEMP_RDY_MASK = (byte)~0b00000010;
68 | static const uint8_t MAX30105_INT_DIE_TEMP_RDY_ENABLE = 0x02;
69 | static const uint8_t MAX30105_INT_DIE_TEMP_RDY_DISABLE = 0x00;
70 |
71 | static const uint8_t MAX30105_SAMPLEAVG_MASK = (byte)~0b11100000;
72 | static const uint8_t MAX30105_SAMPLEAVG_1 = 0x00;
73 | static const uint8_t MAX30105_SAMPLEAVG_2 = 0x20;
74 | static const uint8_t MAX30105_SAMPLEAVG_4 = 0x40;
75 | static const uint8_t MAX30105_SAMPLEAVG_8 = 0x60;
76 | static const uint8_t MAX30105_SAMPLEAVG_16 = 0x80;
77 | static const uint8_t MAX30105_SAMPLEAVG_32 = 0xA0;
78 |
79 | static const uint8_t MAX30105_ROLLOVER_MASK = 0xEF;
80 | static const uint8_t MAX30105_ROLLOVER_ENABLE = 0x10;
81 | static const uint8_t MAX30105_ROLLOVER_DISABLE = 0x00;
82 |
83 | static const uint8_t MAX30105_A_FULL_MASK = 0xF0;
84 |
85 | // Mode configuration commands (page 19)
86 | static const uint8_t MAX30105_SHUTDOWN_MASK = 0x7F;
87 | static const uint8_t MAX30105_SHUTDOWN = 0x80;
88 | static const uint8_t MAX30105_WAKEUP = 0x00;
89 |
90 | static const uint8_t MAX30105_RESET_MASK = 0xBF;
91 | static const uint8_t MAX30105_RESET = 0x40;
92 |
93 | static const uint8_t MAX30105_MODE_MASK = 0xF8;
94 | static const uint8_t MAX30105_MODE_REDONLY = 0x02;
95 | static const uint8_t MAX30105_MODE_REDIRONLY = 0x03;
96 | static const uint8_t MAX30105_MODE_MULTILED = 0x07;
97 |
98 | // Particle sensing configuration commands (pgs 19-20)
99 | static const uint8_t MAX30105_ADCRANGE_MASK = 0x9F;
100 | static const uint8_t MAX30105_ADCRANGE_2048 = 0x00;
101 | static const uint8_t MAX30105_ADCRANGE_4096 = 0x20;
102 | static const uint8_t MAX30105_ADCRANGE_8192 = 0x40;
103 | static const uint8_t MAX30105_ADCRANGE_16384 = 0x60;
104 |
105 | static const uint8_t MAX30105_SAMPLERATE_MASK = 0xE3;
106 | static const uint8_t MAX30105_SAMPLERATE_50 = 0x00;
107 | static const uint8_t MAX30105_SAMPLERATE_100 = 0x04;
108 | static const uint8_t MAX30105_SAMPLERATE_200 = 0x08;
109 | static const uint8_t MAX30105_SAMPLERATE_400 = 0x0C;
110 | static const uint8_t MAX30105_SAMPLERATE_800 = 0x10;
111 | static const uint8_t MAX30105_SAMPLERATE_1000 = 0x14;
112 | static const uint8_t MAX30105_SAMPLERATE_1600 = 0x18;
113 | static const uint8_t MAX30105_SAMPLERATE_3200 = 0x1C;
114 |
115 | static const uint8_t MAX30105_PULSEWIDTH_MASK = 0xFC;
116 | static const uint8_t MAX30105_PULSEWIDTH_69 = 0x00;
117 | static const uint8_t MAX30105_PULSEWIDTH_118 = 0x01;
118 | static const uint8_t MAX30105_PULSEWIDTH_215 = 0x02;
119 | static const uint8_t MAX30105_PULSEWIDTH_411 = 0x03;
120 |
121 | //Multi-LED Mode configuration (pg 22)
122 | static const uint8_t MAX30105_SLOT1_MASK = 0xF8;
123 | static const uint8_t MAX30105_SLOT2_MASK = 0x8F;
124 | static const uint8_t MAX30105_SLOT3_MASK = 0xF8;
125 | static const uint8_t MAX30105_SLOT4_MASK = 0x8F;
126 |
127 | static const uint8_t SLOT_NONE = 0x00;
128 | static const uint8_t SLOT_RED_LED = 0x01;
129 | static const uint8_t SLOT_IR_LED = 0x02;
130 | static const uint8_t SLOT_GREEN_LED = 0x03;
131 | static const uint8_t SLOT_NONE_PILOT = 0x04;
132 | static const uint8_t SLOT_RED_PILOT = 0x05;
133 | static const uint8_t SLOT_IR_PILOT = 0x06;
134 | static const uint8_t SLOT_GREEN_PILOT = 0x07;
135 |
136 | static const uint8_t MAX_30105_EXPECTEDPARTID = 0x15;
137 |
138 | //The MAX30105 stores up to 32 samples on the IC
139 | //This is additional local storage to the microcontroller
140 | const int STORAGE_SIZE = 4; //Each long is 4 bytes so limit this to fit on your micro
141 | struct Record
142 | {
143 | uint32_t red[STORAGE_SIZE];
144 | uint32_t IR[STORAGE_SIZE];
145 | uint32_t green[STORAGE_SIZE];
146 | byte head;
147 | byte tail;
148 | } sense; //This is our circular buffer of readings from the sensor
149 |
150 | MAX30105::MAX30105() {
151 | // Constructor
152 | }
153 |
154 | boolean MAX30105::begin(TwoWire &wirePort, uint32_t i2cSpeed, uint8_t i2caddr) {
155 |
156 | _i2cPort = &wirePort; //Grab which port the user wants us to use
157 |
158 | _i2cPort->begin();
159 | _i2cPort->setClock(i2cSpeed);
160 |
161 | _i2caddr = i2caddr;
162 |
163 | // Step 1: Initial Communciation and Verification
164 | // Check that a MAX30105 is connected
165 | if (!readPartID() == MAX_30105_EXPECTEDPARTID) {
166 | // Error -- Part ID read from MAX30105 does not match expected part ID.
167 | // This may mean there is a physical connectivity problem (broken wire, unpowered, etc).
168 | return false;
169 | }
170 |
171 | // Populate revision ID
172 | readRevisionID();
173 |
174 | return true;
175 | }
176 |
177 | //
178 | // Configuration
179 | //
180 |
181 | //Begin Interrupt configuration
182 | uint8_t MAX30105::getINT1(void) {
183 | return (readRegister8(_i2caddr, MAX30105_INTSTAT1));
184 | }
185 | uint8_t MAX30105::getINT2(void) {
186 | return (readRegister8(_i2caddr, MAX30105_INTSTAT2));
187 | }
188 |
189 | void MAX30105::enableAFULL(void) {
190 | bitMask(MAX30105_INTENABLE1, MAX30105_INT_A_FULL_MASK, MAX30105_INT_A_FULL_ENABLE);
191 | }
192 | void MAX30105::disableAFULL(void) {
193 | bitMask(MAX30105_INTENABLE1, MAX30105_INT_A_FULL_MASK, MAX30105_INT_A_FULL_DISABLE);
194 | }
195 |
196 | void MAX30105::enableDATARDY(void) {
197 | bitMask(MAX30105_INTENABLE1, MAX30105_INT_DATA_RDY_MASK, MAX30105_INT_DATA_RDY_ENABLE);
198 | }
199 | void MAX30105::disableDATARDY(void) {
200 | bitMask(MAX30105_INTENABLE1, MAX30105_INT_DATA_RDY_MASK, MAX30105_INT_DATA_RDY_DISABLE);
201 | }
202 |
203 | void MAX30105::enableALCOVF(void) {
204 | bitMask(MAX30105_INTENABLE1, MAX30105_INT_ALC_OVF_MASK, MAX30105_INT_ALC_OVF_ENABLE);
205 | }
206 | void MAX30105::disableALCOVF(void) {
207 | bitMask(MAX30105_INTENABLE1, MAX30105_INT_ALC_OVF_MASK, MAX30105_INT_ALC_OVF_DISABLE);
208 | }
209 |
210 | void MAX30105::enablePROXINT(void) {
211 | bitMask(MAX30105_INTENABLE1, MAX30105_INT_PROX_INT_MASK, MAX30105_INT_PROX_INT_ENABLE);
212 | }
213 | void MAX30105::disablePROXINT(void) {
214 | bitMask(MAX30105_INTENABLE1, MAX30105_INT_PROX_INT_MASK, MAX30105_INT_PROX_INT_DISABLE);
215 | }
216 |
217 | void MAX30105::enableDIETEMPRDY(void) {
218 | bitMask(MAX30105_INTENABLE2, MAX30105_INT_DIE_TEMP_RDY_MASK, MAX30105_INT_DIE_TEMP_RDY_ENABLE);
219 | }
220 | void MAX30105::disableDIETEMPRDY(void) {
221 | bitMask(MAX30105_INTENABLE2, MAX30105_INT_DIE_TEMP_RDY_MASK, MAX30105_INT_DIE_TEMP_RDY_DISABLE);
222 | }
223 |
224 | //End Interrupt configuration
225 |
226 | void MAX30105::softReset(void) {
227 | bitMask(MAX30105_MODECONFIG, MAX30105_RESET_MASK, MAX30105_RESET);
228 |
229 | // Poll for bit to clear, reset is then complete
230 | // Timeout after 100ms
231 | unsigned long startTime = millis();
232 | while (millis() - startTime < 100)
233 | {
234 | uint8_t response = readRegister8(_i2caddr, MAX30105_MODECONFIG);
235 | if ((response & MAX30105_RESET) == 0) break; //We're done!
236 | delay(1); //Let's not over burden the I2C bus
237 | }
238 | }
239 |
240 | void MAX30105::shutDown(void) {
241 | // Put IC into low power mode (datasheet pg. 19)
242 | // During shutdown the IC will continue to respond to I2C commands but will
243 | // not update with or take new readings (such as temperature)
244 | bitMask(MAX30105_MODECONFIG, MAX30105_SHUTDOWN_MASK, MAX30105_SHUTDOWN);
245 | }
246 |
247 | void MAX30105::wakeUp(void) {
248 | // Pull IC out of low power mode (datasheet pg. 19)
249 | bitMask(MAX30105_MODECONFIG, MAX30105_SHUTDOWN_MASK, MAX30105_WAKEUP);
250 | }
251 |
252 | void MAX30105::setLEDMode(uint8_t mode) {
253 | // Set which LEDs are used for sampling -- Red only, RED+IR only, or custom.
254 | // See datasheet, page 19
255 | bitMask(MAX30105_MODECONFIG, MAX30105_MODE_MASK, mode);
256 | }
257 |
258 | void MAX30105::setADCRange(uint8_t adcRange) {
259 | // adcRange: one of MAX30105_ADCRANGE_2048, _4096, _8192, _16384
260 | bitMask(MAX30105_PARTICLECONFIG, MAX30105_ADCRANGE_MASK, adcRange);
261 | }
262 |
263 | void MAX30105::setSampleRate(uint8_t sampleRate) {
264 | // sampleRate: one of MAX30105_SAMPLERATE_50, _100, _200, _400, _800, _1000, _1600, _3200
265 | bitMask(MAX30105_PARTICLECONFIG, MAX30105_SAMPLERATE_MASK, sampleRate);
266 | }
267 |
268 | void MAX30105::setPulseWidth(uint8_t pulseWidth) {
269 | // pulseWidth: one of MAX30105_PULSEWIDTH_69, _188, _215, _411
270 | bitMask(MAX30105_PARTICLECONFIG, MAX30105_PULSEWIDTH_MASK, pulseWidth);
271 | }
272 |
273 | // NOTE: Amplitude values: 0x00 = 0mA, 0x7F = 25.4mA, 0xFF = 50mA (typical)
274 | // See datasheet, page 21
275 | void MAX30105::setPulseAmplitudeRed(uint8_t amplitude) {
276 | writeRegister8(_i2caddr, MAX30105_LED1_PULSEAMP, amplitude);
277 | }
278 |
279 | void MAX30105::setPulseAmplitudeIR(uint8_t amplitude) {
280 | writeRegister8(_i2caddr, MAX30105_LED2_PULSEAMP, amplitude);
281 | }
282 |
283 | void MAX30105::setPulseAmplitudeGreen(uint8_t amplitude) {
284 | writeRegister8(_i2caddr, MAX30105_LED3_PULSEAMP, amplitude);
285 | }
286 |
287 | void MAX30105::setPulseAmplitudeProximity(uint8_t amplitude) {
288 | writeRegister8(_i2caddr, MAX30105_LED_PROX_AMP, amplitude);
289 | }
290 |
291 | void MAX30105::setProximityThreshold(uint8_t threshMSB) {
292 | // Set the IR ADC count that will trigger the beginning of particle-sensing mode.
293 | // The threshMSB signifies only the 8 most significant-bits of the ADC count.
294 | // See datasheet, page 24.
295 | writeRegister8(_i2caddr, MAX30105_PROXINTTHRESH, threshMSB);
296 | }
297 |
298 | //Given a slot number assign a thing to it
299 | //Devices are SLOT_RED_LED or SLOT_RED_PILOT (proximity)
300 | //Assigning a SLOT_RED_LED will pulse LED
301 | //Assigning a SLOT_RED_PILOT will ??
302 | void MAX30105::enableSlot(uint8_t slotNumber, uint8_t device) {
303 |
304 | uint8_t originalContents;
305 |
306 | switch (slotNumber) {
307 | case (1):
308 | bitMask(MAX30105_MULTILEDCONFIG1, MAX30105_SLOT1_MASK, device);
309 | break;
310 | case (2):
311 | bitMask(MAX30105_MULTILEDCONFIG1, MAX30105_SLOT2_MASK, device << 4);
312 | break;
313 | case (3):
314 | bitMask(MAX30105_MULTILEDCONFIG2, MAX30105_SLOT3_MASK, device);
315 | break;
316 | case (4):
317 | bitMask(MAX30105_MULTILEDCONFIG2, MAX30105_SLOT4_MASK, device << 4);
318 | break;
319 | default:
320 | //Shouldn't be here!
321 | break;
322 | }
323 | }
324 |
325 | //Clears all slot assignments
326 | void MAX30105::disableSlots(void) {
327 | writeRegister8(_i2caddr, MAX30105_MULTILEDCONFIG1, 0);
328 | writeRegister8(_i2caddr, MAX30105_MULTILEDCONFIG2, 0);
329 | }
330 |
331 | //
332 | // FIFO Configuration
333 | //
334 |
335 | //Set sample average (Table 3, Page 18)
336 | void MAX30105::setFIFOAverage(uint8_t numberOfSamples) {
337 | bitMask(MAX30105_FIFOCONFIG, MAX30105_SAMPLEAVG_MASK, numberOfSamples);
338 | }
339 |
340 | //Resets all points to start in a known state
341 | //Page 15 recommends clearing FIFO before beginning a read
342 | void MAX30105::clearFIFO(void) {
343 | writeRegister8(_i2caddr, MAX30105_FIFOWRITEPTR, 0);
344 | writeRegister8(_i2caddr, MAX30105_FIFOOVERFLOW, 0);
345 | writeRegister8(_i2caddr, MAX30105_FIFOREADPTR, 0);
346 | }
347 |
348 | //Enable roll over if FIFO over flows
349 | void MAX30105::enableFIFORollover(void) {
350 | bitMask(MAX30105_FIFOCONFIG, MAX30105_ROLLOVER_MASK, MAX30105_ROLLOVER_ENABLE);
351 | }
352 |
353 | //Disable roll over if FIFO over flows
354 | void MAX30105::disableFIFORollover(void) {
355 | bitMask(MAX30105_FIFOCONFIG, MAX30105_ROLLOVER_MASK, MAX30105_ROLLOVER_DISABLE);
356 | }
357 |
358 | //Set number of samples to trigger the almost full interrupt (Page 18)
359 | //Power on default is 32 samples
360 | //Note it is reverse: 0x00 is 32 samples, 0x0F is 17 samples
361 | void MAX30105::setFIFOAlmostFull(uint8_t numberOfSamples) {
362 | bitMask(MAX30105_FIFOCONFIG, MAX30105_A_FULL_MASK, numberOfSamples);
363 | }
364 |
365 | //Read the FIFO Write Pointer
366 | uint8_t MAX30105::getWritePointer(void) {
367 | return (readRegister8(_i2caddr, MAX30105_FIFOWRITEPTR));
368 | }
369 |
370 | //Read the FIFO Read Pointer
371 | uint8_t MAX30105::getReadPointer(void) {
372 | return (readRegister8(_i2caddr, MAX30105_FIFOREADPTR));
373 | }
374 |
375 |
376 | // Die Temperature
377 | // Returns temp in C
378 | float MAX30105::readTemperature() {
379 | // Step 1: Config die temperature register to take 1 temperature sample
380 | writeRegister8(_i2caddr, MAX30105_DIETEMPCONFIG, 0x01);
381 |
382 | // Poll for bit to clear, reading is then complete
383 | // Timeout after 100ms
384 | unsigned long startTime = millis();
385 | while (millis() - startTime < 100)
386 | {
387 | uint8_t response = readRegister8(_i2caddr, MAX30105_DIETEMPCONFIG);
388 | if ((response & 0x01) == 0) break; //We're done!
389 | delay(1); //Let's not over burden the I2C bus
390 | }
391 | //TODO How do we want to fail? With what type of error?
392 | //? if(millis() - startTime >= 100) return(-999.0);
393 |
394 | // Step 2: Read die temperature register (integer)
395 | int8_t tempInt = readRegister8(_i2caddr, MAX30105_DIETEMPINT);
396 | uint8_t tempFrac = readRegister8(_i2caddr, MAX30105_DIETEMPFRAC);
397 |
398 | // Step 3: Calculate temperature (datasheet pg. 23)
399 | return (float)tempInt + ((float)tempFrac * 0.0625);
400 | }
401 |
402 | // Returns die temp in F
403 | float MAX30105::readTemperatureF() {
404 | float temp = readTemperature();
405 |
406 | if (temp != -999.0) temp = temp * 1.8 + 32.0;
407 |
408 | return (temp);
409 | }
410 |
411 | // Set the PROX_INT_THRESHold
412 | void MAX30105::setPROXINTTHRESH(uint8_t val) {
413 | writeRegister8(_i2caddr, MAX30105_PROXINTTHRESH, val);
414 | }
415 |
416 |
417 | //
418 | // Device ID and Revision
419 | //
420 | uint8_t MAX30105::readPartID() {
421 | return readRegister8(_i2caddr, MAX30105_PARTID);
422 | }
423 |
424 | void MAX30105::readRevisionID() {
425 | revisionID = readRegister8(_i2caddr, MAX30105_REVISIONID);
426 | }
427 |
428 | uint8_t MAX30105::getRevisionID() {
429 | return revisionID;
430 | }
431 |
432 |
433 | //Setup the sensor
434 | //The MAX30105 has many settings. By default we select:
435 | // Sample Average = 4
436 | // Mode = MultiLED
437 | // ADC Range = 16384 (62.5pA per LSB)
438 | // Sample rate = 50
439 | //Use the default setup if you are just getting started with the MAX30105 sensor
440 | void MAX30105::setup(byte powerLevel, byte sampleAverage, byte ledMode, int sampleRate, int pulseWidth, int adcRange) {
441 | softReset(); //Reset all configuration, threshold, and data registers to POR values
442 |
443 | //FIFO Configuration
444 | //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
445 | //The chip will average multiple samples of same type together if you wish
446 | if (sampleAverage == 1) setFIFOAverage(MAX30105_SAMPLEAVG_1); //No averaging per FIFO record
447 | else if (sampleAverage == 2) setFIFOAverage(MAX30105_SAMPLEAVG_2);
448 | else if (sampleAverage == 4) setFIFOAverage(MAX30105_SAMPLEAVG_4);
449 | else if (sampleAverage == 8) setFIFOAverage(MAX30105_SAMPLEAVG_8);
450 | else if (sampleAverage == 16) setFIFOAverage(MAX30105_SAMPLEAVG_16);
451 | else if (sampleAverage == 32) setFIFOAverage(MAX30105_SAMPLEAVG_32);
452 | else setFIFOAverage(MAX30105_SAMPLEAVG_4);
453 |
454 | //setFIFOAlmostFull(2); //Set to 30 samples to trigger an 'Almost Full' interrupt
455 | enableFIFORollover(); //Allow FIFO to wrap/roll over
456 | //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
457 |
458 | //Mode Configuration
459 | //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
460 | if (ledMode == 3) setLEDMode(MAX30105_MODE_MULTILED); //Watch all three LED channels
461 | else if (ledMode == 2) setLEDMode(MAX30105_MODE_REDIRONLY); //Red and IR
462 | else setLEDMode(MAX30105_MODE_REDONLY); //Red only
463 | activeLEDs = ledMode; //Used to control how many bytes to read from FIFO buffer
464 | //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
465 |
466 | //Particle Sensing Configuration
467 | //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
468 | if(adcRange < 4096) setADCRange(MAX30105_ADCRANGE_2048); //7.81pA per LSB
469 | else if(adcRange < 8192) setADCRange(MAX30105_ADCRANGE_4096); //15.63pA per LSB
470 | else if(adcRange < 16384) setADCRange(MAX30105_ADCRANGE_8192); //31.25pA per LSB
471 | else if(adcRange == 16384) setADCRange(MAX30105_ADCRANGE_16384); //62.5pA per LSB
472 | else setADCRange(MAX30105_ADCRANGE_2048);
473 |
474 | if (sampleRate < 100) setSampleRate(MAX30105_SAMPLERATE_50); //Take 50 samples per second
475 | else if (sampleRate < 200) setSampleRate(MAX30105_SAMPLERATE_100);
476 | else if (sampleRate < 400) setSampleRate(MAX30105_SAMPLERATE_200);
477 | else if (sampleRate < 800) setSampleRate(MAX30105_SAMPLERATE_400);
478 | else if (sampleRate < 1000) setSampleRate(MAX30105_SAMPLERATE_800);
479 | else if (sampleRate < 1600) setSampleRate(MAX30105_SAMPLERATE_1000);
480 | else if (sampleRate < 3200) setSampleRate(MAX30105_SAMPLERATE_1600);
481 | else if (sampleRate == 3200) setSampleRate(MAX30105_SAMPLERATE_3200);
482 | else setSampleRate(MAX30105_SAMPLERATE_50);
483 |
484 | //The longer the pulse width the longer range of detection you'll have
485 | //At 69us and 0.4mA it's about 2 inches
486 | //At 411us and 0.4mA it's about 6 inches
487 | if (pulseWidth < 118) setPulseWidth(MAX30105_PULSEWIDTH_69); //Page 26, Gets us 15 bit resolution
488 | else if (pulseWidth < 215) setPulseWidth(MAX30105_PULSEWIDTH_118); //16 bit resolution
489 | else if (pulseWidth < 411) setPulseWidth(MAX30105_PULSEWIDTH_215); //17 bit resolution
490 | else if (pulseWidth == 411) setPulseWidth(MAX30105_PULSEWIDTH_411); //18 bit resolution
491 | else setPulseWidth(MAX30105_PULSEWIDTH_69);
492 | //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
493 |
494 | //LED Pulse Amplitude Configuration
495 | //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
496 | //Default is 0x1F which gets us 6.4mA
497 | //powerLevel = 0x02, 0.4mA - Presence detection of ~4 inch
498 | //powerLevel = 0x1F, 6.4mA - Presence detection of ~8 inch
499 | //powerLevel = 0x7F, 25.4mA - Presence detection of ~8 inch
500 | //powerLevel = 0xFF, 50.0mA - Presence detection of ~12 inch
501 |
502 | setPulseAmplitudeRed(powerLevel);
503 | setPulseAmplitudeIR(powerLevel);
504 | setPulseAmplitudeGreen(powerLevel);
505 | setPulseAmplitudeProximity(powerLevel);
506 | //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
507 |
508 | //Multi-LED Mode Configuration, Enable the reading of the three LEDs
509 | //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
510 | enableSlot(1, SLOT_RED_LED);
511 | if (ledMode > 1) enableSlot(2, SLOT_IR_LED);
512 | if (ledMode > 2) enableSlot(3, SLOT_GREEN_LED);
513 | //enableSlot(1, SLOT_RED_PILOT);
514 | //enableSlot(2, SLOT_IR_PILOT);
515 | //enableSlot(3, SLOT_GREEN_PILOT);
516 | //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
517 |
518 | clearFIFO(); //Reset the FIFO before we begin checking the sensor
519 | }
520 |
521 | //
522 | // Data Collection
523 | //
524 |
525 | //Tell caller how many samples are available
526 | uint8_t MAX30105::available(void)
527 | {
528 | uint8_t numberOfSamples = sense.head - sense.tail;
529 | if (numberOfSamples < 0) numberOfSamples += STORAGE_SIZE;
530 |
531 | return (numberOfSamples);
532 | }
533 |
534 | //Report the most recent red value
535 | uint32_t MAX30105::getRed(void)
536 | {
537 | //Check the sensor for new data for 250ms
538 | if(safeCheck(250))
539 | return (sense.red[sense.head]);
540 | else
541 | return(0); //Sensor failed to find new data
542 | }
543 |
544 | //Report the most recent IR value
545 | uint32_t MAX30105::getIR(void)
546 | {
547 | //Check the sensor for new data for 250ms
548 | if(safeCheck(250))
549 | return (sense.IR[sense.head]);
550 | else
551 | return(0); //Sensor failed to find new data
552 | }
553 |
554 | //Report the most recent Green value
555 | uint32_t MAX30105::getGreen(void)
556 | {
557 | //Check the sensor for new data for 250ms
558 | if(safeCheck(250))
559 | return (sense.green[sense.head]);
560 | else
561 | return(0); //Sensor failed to find new data
562 | }
563 |
564 | //Report the next Red value in the FIFO
565 | uint32_t MAX30105::getFIFORed(void)
566 | {
567 | return (sense.red[sense.tail]);
568 | }
569 |
570 | //Report the next IR value in the FIFO
571 | uint32_t MAX30105::getFIFOIR(void)
572 | {
573 | return (sense.IR[sense.tail]);
574 | }
575 |
576 | //Report the next Green value in the FIFO
577 | uint32_t MAX30105::getFIFOGreen(void)
578 | {
579 | return (sense.green[sense.tail]);
580 | }
581 |
582 | //Advance the tail
583 | void MAX30105::nextSample(void)
584 | {
585 | if(available()) //Only advance the tail if new data is available
586 | {
587 | sense.tail++;
588 | sense.tail %= STORAGE_SIZE; //Wrap condition
589 | }
590 | }
591 |
592 | //Polls the sensor for new data
593 | //Call regularly
594 | //If new data is available, it updates the head and tail in the main struct
595 | //Returns number of new samples obtained
596 | uint16_t MAX30105::check(void)
597 | {
598 | //Read register FIDO_DATA in (3-byte * number of active LED) chunks
599 | //Until FIFO_RD_PTR = FIFO_WR_PTR
600 |
601 | byte readPointer = getReadPointer();
602 | byte writePointer = getWritePointer();
603 |
604 | int numberOfSamples = 0;
605 |
606 | //Do we have new data?
607 | if (readPointer != writePointer)
608 | {
609 | //Calculate the number of readings we need to get from sensor
610 | numberOfSamples = writePointer - readPointer;
611 | if (numberOfSamples < 0) numberOfSamples += 32; //Wrap condition
612 |
613 | //We now have the number of readings, now calc bytes to read
614 | //For this example we are just doing Red and IR (3 bytes each)
615 | int bytesLeftToRead = numberOfSamples * activeLEDs * 3;
616 |
617 | //Get ready to read a burst of data from the FIFO register
618 | _i2cPort->beginTransmission(MAX30105_ADDRESS);
619 | _i2cPort->write(MAX30105_FIFODATA);
620 | _i2cPort->endTransmission();
621 |
622 | //Wire.requestFrom() is limited to BUFFER_LENGTH which is 32 on the Uno
623 | //We may need to read as many as 288 bytes so we read in blocks no larger than 32
624 | //BUFFER_LENGTH should work with other platforms with larger requestFrom buffers
625 | while (bytesLeftToRead > 0)
626 | {
627 | int toGet = bytesLeftToRead;
628 | if (toGet > BUFFER_LENGTH)
629 | {
630 | //If toGet is 32 this is bad because we read 6 bytes (Red+IR * 3 = 6) at a time
631 | //32 % 6 = 2 left over. We don't want to request 32 bytes, we want to request 30.
632 | //32 % 9 (Red+IR+GREEN) = 5 left over. We want to request 27.
633 |
634 | toGet = BUFFER_LENGTH - (BUFFER_LENGTH % (activeLEDs * 3)); //Trim toGet to be a multiple of the samples we need to read
635 | }
636 |
637 | bytesLeftToRead -= toGet;
638 |
639 | //Request toGet number of bytes from sensor
640 | _i2cPort->requestFrom(MAX30105_ADDRESS, toGet);
641 |
642 | while (toGet > 0)
643 | {
644 | sense.head++; //Advance the head of the storage struct
645 | sense.head %= STORAGE_SIZE; //Wrap condition
646 |
647 | byte temp[sizeof(uint32_t)]; //Array of 4 bytes that we will convert into long
648 | uint32_t tempLong;
649 |
650 | //Burst read three bytes - RED
651 | temp[3] = 0;
652 | temp[2] = _i2cPort->read();
653 | temp[1] = _i2cPort->read();
654 | temp[0] = _i2cPort->read();
655 |
656 | //Convert array to long
657 | memcpy(&tempLong, temp, sizeof(tempLong));
658 |
659 | tempLong &= 0x3FFFF; //Zero out all but 18 bits
660 |
661 | sense.red[sense.head] = tempLong; //Store this reading into the sense array
662 |
663 | if (activeLEDs > 1)
664 | {
665 | //Burst read three more bytes - IR
666 | temp[3] = 0;
667 | temp[2] = _i2cPort->read();
668 | temp[1] = _i2cPort->read();
669 | temp[0] = _i2cPort->read();
670 |
671 | //Convert array to long
672 | memcpy(&tempLong, temp, sizeof(tempLong));
673 |
674 | tempLong &= 0x3FFFF; //Zero out all but 18 bits
675 |
676 | sense.IR[sense.head] = tempLong;
677 | }
678 |
679 | if (activeLEDs > 2)
680 | {
681 | //Burst read three more bytes - Green
682 | temp[3] = 0;
683 | temp[2] = _i2cPort->read();
684 | temp[1] = _i2cPort->read();
685 | temp[0] = _i2cPort->read();
686 |
687 | //Convert array to long
688 | memcpy(&tempLong, temp, sizeof(tempLong));
689 |
690 | tempLong &= 0x3FFFF; //Zero out all but 18 bits
691 |
692 | sense.green[sense.head] = tempLong;
693 | }
694 |
695 | toGet -= activeLEDs * 3;
696 | }
697 |
698 | } //End while (bytesLeftToRead > 0)
699 |
700 | } //End readPtr != writePtr
701 |
702 | return (numberOfSamples); //Let the world know how much new data we found
703 | }
704 |
705 | //Check for new data but give up after a certain amount of time
706 | //Returns true if new data was found
707 | //Returns false if new data was not found
708 | bool MAX30105::safeCheck(uint8_t maxTimeToCheck)
709 | {
710 | uint32_t markTime = millis();
711 |
712 | while(1)
713 | {
714 | if(millis() - markTime > maxTimeToCheck) return(false);
715 |
716 | if(check() == true) //We found new data!
717 | return(true);
718 |
719 | delay(1);
720 | }
721 | }
722 |
723 | //Given a register, read it, mask it, and then set the thing
724 | void MAX30105::bitMask(uint8_t reg, uint8_t mask, uint8_t thing)
725 | {
726 | // Grab current register context
727 | uint8_t originalContents = readRegister8(_i2caddr, reg);
728 |
729 | // Zero-out the portions of the register we're interested in
730 | originalContents = originalContents & mask;
731 |
732 | // Change contents
733 | writeRegister8(_i2caddr, reg, originalContents | thing);
734 | }
735 |
736 | //
737 | // Low-level I2C Communication
738 | //
739 | uint8_t MAX30105::readRegister8(uint8_t address, uint8_t reg) {
740 | _i2cPort->beginTransmission(address);
741 | _i2cPort->write(reg);
742 | _i2cPort->endTransmission(false);
743 | _i2cPort->requestFrom(address, 1); // Request 1 byte
744 |
745 | int tries = 0;
746 | while (!_i2cPort->available())
747 | {
748 | delay(1);
749 | if (tries++ > 200) break;
750 | }
751 | if (tries == 200) return (0); //Fail
752 |
753 | return (_i2cPort->read());
754 | }
755 |
756 | void MAX30105::writeRegister8(uint8_t address, uint8_t reg, uint8_t value) {
757 | _i2cPort->beginTransmission(address);
758 | _i2cPort->write(reg);
759 | _i2cPort->write(value);
760 | _i2cPort->endTransmission();
761 | }
762 |
--------------------------------------------------------------------------------
/Libraries/Arduino/src/MAX30105.h:
--------------------------------------------------------------------------------
1 | /***************************************************
2 | This is a library written for the Maxim MAX30105 Optical Smoke Detector
3 | It should also work with the MAX30102. However, the MAX30102 does not have a Green LED.
4 |
5 | These sensors use I2C to communicate, as well as a single (optional)
6 | interrupt line that is not currently supported in this driver.
7 |
8 | Written by Peter Jansen and Nathan Seidle (SparkFun)
9 | BSD license, all text above must be included in any redistribution.
10 | *****************************************************/
11 |
12 | #pragma once
13 |
14 | #if (ARDUINO >= 100)
15 | #include "Arduino.h"
16 | #else
17 | #include "WProgram.h"
18 | #endif
19 |
20 | #include
21 |
22 | #define MAX30105_ADDRESS 0x57 //7-bit I2C Address
23 | //Note that MAX30102 has the same I2C address and Part ID
24 |
25 | #define I2C_SPEED_STANDARD 100000
26 | #define I2C_SPEED_FAST 400000
27 |
28 | class MAX30105 {
29 | public:
30 | MAX30105(void);
31 |
32 | boolean begin(TwoWire &wirePort = Wire, uint32_t i2cSpeed = I2C_SPEED_STANDARD, uint8_t i2caddr = MAX30105_ADDRESS);
33 |
34 | uint32_t getRed(void); //Returns immediate red value
35 | uint32_t getIR(void); //Returns immediate IR value
36 | uint32_t getGreen(void); //Returns immediate green value
37 | bool safeCheck(uint8_t maxTimeToCheck); //Given a max amount of time, check for new data
38 |
39 | // Configuration
40 | void softReset();
41 | void shutDown();
42 | void wakeUp();
43 |
44 | void setLEDMode(uint8_t mode);
45 |
46 | void setADCRange(uint8_t adcRange);
47 | void setSampleRate(uint8_t sampleRate);
48 | void setPulseWidth(uint8_t pulseWidth);
49 |
50 | void setPulseAmplitudeRed(uint8_t value);
51 | void setPulseAmplitudeIR(uint8_t value);
52 | void setPulseAmplitudeGreen(uint8_t value);
53 | void setPulseAmplitudeProximity(uint8_t value);
54 |
55 | void setProximityThreshold(uint8_t threshMSB);
56 |
57 | //Multi-led configuration mode (page 22)
58 | void enableSlot(uint8_t slotNumber, uint8_t device); //Given slot number, assign a device to slot
59 | void disableSlots(void);
60 |
61 | // Data Collection
62 |
63 | //Interrupts (page 13, 14)
64 | uint8_t getINT1(void); //Returns the main interrupt group
65 | uint8_t getINT2(void); //Returns the temp ready interrupt
66 | void enableAFULL(void); //Enable/disable individual interrupts
67 | void disableAFULL(void);
68 | void enableDATARDY(void);
69 | void disableDATARDY(void);
70 | void enableALCOVF(void);
71 | void disableALCOVF(void);
72 | void enablePROXINT(void);
73 | void disablePROXINT(void);
74 | void enableDIETEMPRDY(void);
75 | void disableDIETEMPRDY(void);
76 |
77 | //FIFO Configuration (page 18)
78 | void setFIFOAverage(uint8_t samples);
79 | void enableFIFORollover();
80 | void disableFIFORollover();
81 | void setFIFOAlmostFull(uint8_t samples);
82 |
83 | //FIFO Reading
84 | uint16_t check(void); //Checks for new data and fills FIFO
85 | uint8_t available(void); //Tells caller how many new samples are available (head - tail)
86 | void nextSample(void); //Advances the tail of the sense array
87 | uint32_t getFIFORed(void); //Returns the FIFO sample pointed to by tail
88 | uint32_t getFIFOIR(void); //Returns the FIFO sample pointed to by tail
89 | uint32_t getFIFOGreen(void); //Returns the FIFO sample pointed to by tail
90 |
91 | uint8_t getWritePointer(void);
92 | uint8_t getReadPointer(void);
93 | void clearFIFO(void); //Sets the read/write pointers to zero
94 |
95 | //Proximity Mode Interrupt Threshold
96 | void setPROXINTTHRESH(uint8_t val);
97 |
98 | // Die Temperature
99 | float readTemperature();
100 | float readTemperatureF();
101 |
102 | // Detecting ID/Revision
103 | uint8_t getRevisionID();
104 | uint8_t readPartID();
105 |
106 | // Setup the IC with user selectable settings
107 | void setup(byte powerLevel = 0x1F, byte sampleAverage = 4, byte ledMode = 3, int sampleRate = 400, int pulseWidth = 411, int adcRange = 4096);
108 |
109 | // Low-level I2C communication
110 | uint8_t readRegister8(uint8_t address, uint8_t reg);
111 | void writeRegister8(uint8_t address, uint8_t reg, uint8_t value);
112 |
113 | private:
114 | TwoWire *_i2cPort; //The generic connection to user's chosen I2C hardware
115 | uint8_t _i2caddr;
116 |
117 | //activeLEDs is the number of channels turned on, and can be 1 to 3. 2 is common for Red+IR.
118 | byte activeLEDs; //Gets set during setup. Allows check() to calculate how many bytes to read from FIFO
119 |
120 | uint8_t revisionID;
121 |
122 | void readRevisionID();
123 |
124 | void bitMask(uint8_t reg, uint8_t mask, uint8_t thing);
125 | };
126 |
--------------------------------------------------------------------------------
/Libraries/Arduino/src/heartRate.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | Optical Heart Rate Detection (PBA Algorithm)
3 | By: Nathan Seidle
4 | SparkFun Electronics
5 | Date: October 2nd, 2016
6 |
7 | Given a series of IR samples from the MAX30105 we discern when a heart beat is occurring
8 |
9 | Let's have a brief chat about what this code does. We're going to try to detect
10 | heart-rate optically. This is tricky and prone to give false readings. We really don't
11 | want to get anyone hurt so use this code only as an example of how to process optical
12 | data. Build fun stuff with our MAX30105 breakout board but don't use it for actual
13 | medical diagnosis.
14 |
15 | Excellent background on optical heart rate detection:
16 | http://www.ti.com/lit/an/slaa655/slaa655.pdf
17 |
18 | Good reading:
19 | http://www.techforfuture.nl/fjc_documents/mitrabaratchi-measuringheartratewithopticalsensor.pdf
20 | https://fruct.org/publications/fruct13/files/Lau.pdf
21 |
22 | This is an implementation of Maxim's PBA (Penpheral Beat Amplitude) algorithm. It's been
23 | converted to work within the Arduino framework.
24 | */
25 |
26 | /* Copyright (C) 2016 Maxim Integrated Products, Inc., All Rights Reserved.
27 | *
28 | * Permission is hereby granted, free of charge, to any person obtaining a
29 | * copy of this software and associated documentation files (the "Software"),
30 | * to deal in the Software without restriction, including without limitation
31 | * the rights to use, copy, modify, merge, publish, distribute, sublicense,
32 | * and/or sell copies of the Software, and to permit persons to whom the
33 | * Software is furnished to do so, subject to the following conditions:
34 | *
35 | * The above copyright notice and this permission notice shall be included
36 | * in all copies or substantial portions of the Software.
37 | *
38 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
39 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
40 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
41 | * IN NO EVENT SHALL MAXIM INTEGRATED BE LIABLE FOR ANY CLAIM, DAMAGES
42 | * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
43 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
44 | * OTHER DEALINGS IN THE SOFTWARE.
45 | *
46 | * Except as contained in this notice, the name of Maxim Integrated
47 | * Products, Inc. shall not be used except as stated in the Maxim Integrated
48 | * Products, Inc. Branding Policy.
49 | *
50 | * The mere transfer of this software does not imply any licenses
51 | * of trade secrets, proprietary technology, copyrights, patents,
52 | * trademarks, maskwork rights, or any other form of intellectual
53 | * property whatsoever. Maxim Integrated Products, Inc. retains all
54 | * ownership rights.
55 | *
56 | */
57 |
58 | #include "heartRate.h"
59 |
60 | int16_t IR_AC_Max = 20;
61 | int16_t IR_AC_Min = -20;
62 |
63 | int16_t IR_AC_Signal_Current = 0;
64 | int16_t IR_AC_Signal_Previous;
65 | int16_t IR_AC_Signal_min = 0;
66 | int16_t IR_AC_Signal_max = 0;
67 | int16_t IR_Average_Estimated;
68 |
69 | int16_t positiveEdge = 0;
70 | int16_t negativeEdge = 0;
71 | int32_t ir_avg_reg = 0;
72 |
73 | int16_t cbuf[32];
74 | uint8_t offset = 0;
75 |
76 | static const uint16_t FIRCoeffs[12] = {172, 321, 579, 927, 1360, 1858, 2390, 2916, 3391, 3768, 4012, 4096};
77 |
78 | // Heart Rate Monitor functions takes a sample value and the sample number
79 | // Returns true if a beat is detected
80 | // A running average of four samples is recommended for display on the screen.
81 | bool checkForBeat(int32_t sample)
82 | {
83 | bool beatDetected = false;
84 |
85 | // Save current state
86 | IR_AC_Signal_Previous = IR_AC_Signal_Current;
87 |
88 | //This is good to view for debugging
89 | //Serial.print("Signal_Current: ");
90 | //Serial.println(IR_AC_Signal_Current);
91 |
92 | // Process next data sample
93 | IR_Average_Estimated = averageDCEstimator(&ir_avg_reg, sample);
94 | IR_AC_Signal_Current = lowPassFIRFilter(sample - IR_Average_Estimated);
95 |
96 | // Detect positive zero crossing (rising edge)
97 | if ((IR_AC_Signal_Previous < 0) & (IR_AC_Signal_Current >= 0))
98 | {
99 |
100 | IR_AC_Max = IR_AC_Signal_max; //Adjust our AC max and min
101 | IR_AC_Min = IR_AC_Signal_min;
102 |
103 | positiveEdge = 1;
104 | negativeEdge = 0;
105 | IR_AC_Signal_max = 0;
106 |
107 | //if ((IR_AC_Max - IR_AC_Min) > 100 & (IR_AC_Max - IR_AC_Min) < 1000)
108 | if ((IR_AC_Max - IR_AC_Min) > 20 & (IR_AC_Max - IR_AC_Min) < 1000)
109 | {
110 | //Heart beat!!!
111 | beatDetected = true;
112 | }
113 | }
114 |
115 | // Detect negative zero crossing (falling edge)
116 | if ((IR_AC_Signal_Previous > 0) & (IR_AC_Signal_Current <= 0))
117 | {
118 | positiveEdge = 0;
119 | negativeEdge = 1;
120 | IR_AC_Signal_min = 0;
121 | }
122 |
123 | // Find Maximum value in positive cycle
124 | if (positiveEdge & (IR_AC_Signal_Current > IR_AC_Signal_Previous))
125 | {
126 | IR_AC_Signal_max = IR_AC_Signal_Current;
127 | }
128 |
129 | // Find Minimum value in negative cycle
130 | if (negativeEdge & (IR_AC_Signal_Current < IR_AC_Signal_Previous))
131 | {
132 | IR_AC_Signal_min = IR_AC_Signal_Current;
133 | }
134 |
135 | return(beatDetected);
136 | }
137 |
138 | // Average DC Estimator
139 | int16_t averageDCEstimator(int32_t *p, uint16_t x)
140 | {
141 | *p += ((((long) x << 15) - *p) >> 4);
142 | return (*p >> 15);
143 | }
144 |
145 | // Low Pass FIR Filter
146 | int16_t lowPassFIRFilter(int16_t din)
147 | {
148 | cbuf[offset] = din;
149 |
150 | int32_t z = mul16(FIRCoeffs[11], cbuf[(offset - 11) & 0x1F]);
151 |
152 | for (uint8_t i = 0 ; i < 11 ; i++)
153 | {
154 | z += mul16(FIRCoeffs[i], cbuf[(offset - i) & 0x1F] + cbuf[(offset - 22 + i) & 0x1F]);
155 | }
156 |
157 | offset++;
158 | offset %= 32; //Wrap condition
159 |
160 | return(z >> 15);
161 | }
162 |
163 | // Integer multiplier
164 | int32_t mul16(int16_t x, int16_t y)
165 | {
166 | return((long)x * (long)y);
167 | }
168 |
--------------------------------------------------------------------------------
/Libraries/Arduino/src/heartRate.h:
--------------------------------------------------------------------------------
1 | /*
2 | Optical Heart Rate Detection (PBA Algorithm)
3 | By: Nathan Seidle
4 | SparkFun Electronics
5 | Date: October 2nd, 2016
6 |
7 | Given a series of IR samples from the MAX30105 we discern when a heart beat is occurring
8 |
9 | Let's have a brief chat about what this code does. We're going to try to detect
10 | heart-rate optically. This is tricky and prone to give false readings. We really don't
11 | want to get anyone hurt so use this code only as an example of how to process optical
12 | data. Build fun stuff with our MAX30105 breakout board but don't use it for actual
13 | medical diagnosis.
14 |
15 | Excellent background on optical heart rate detection:
16 | http://www.ti.com/lit/an/slaa655/slaa655.pdf
17 |
18 | Good reading:
19 | http://www.techforfuture.nl/fjc_documents/mitrabaratchi-measuringheartratewithopticalsensor.pdf
20 | https://fruct.org/publications/fruct13/files/Lau.pdf
21 |
22 | This is an implementation of Maxim's PBA (Penpheral Beat Amplitude) algorithm. It's been
23 | converted to work within the Arduino framework.
24 | */
25 |
26 | /* Copyright (C) 2016 Maxim Integrated Products, Inc., All Rights Reserved.
27 | *
28 | * Permission is hereby granted, free of charge, to any person obtaining a
29 | * copy of this software and associated documentation files (the "Software"),
30 | * to deal in the Software without restriction, including without limitation
31 | * the rights to use, copy, modify, merge, publish, distribute, sublicense,
32 | * and/or sell copies of the Software, and to permit persons to whom the
33 | * Software is furnished to do so, subject to the following conditions:
34 | *
35 | * The above copyright notice and this permission notice shall be included
36 | * in all copies or substantial portions of the Software.
37 | *
38 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
39 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
40 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
41 | * IN NO EVENT SHALL MAXIM INTEGRATED BE LIABLE FOR ANY CLAIM, DAMAGES
42 | * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
43 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
44 | * OTHER DEALINGS IN THE SOFTWARE.
45 | *
46 | * Except as contained in this notice, the name of Maxim Integrated
47 | * Products, Inc. shall not be used except as stated in the Maxim Integrated
48 | * Products, Inc. Branding Policy.
49 | *
50 | * The mere transfer of this software does not imply any licenses
51 | * of trade secrets, proprietary technology, copyrights, patents,
52 | * trademarks, maskwork rights, or any other form of intellectual
53 | * property whatsoever. Maxim Integrated Products, Inc. retains all
54 | * ownership rights.
55 | *
56 | */
57 |
58 | #if (ARDUINO >= 100)
59 | #include "Arduino.h"
60 | #else
61 | #include "WProgram.h"
62 | #endif
63 |
64 | bool checkForBeat(int32_t sample);
65 | int16_t averageDCEstimator(int32_t *p, uint16_t x);
66 | int16_t lowPassFIRFilter(int16_t din);
67 | int32_t mul16(int16_t x, int16_t y);
68 |
--------------------------------------------------------------------------------
/Libraries/Arduino/src/spo2_algorithm.cpp:
--------------------------------------------------------------------------------
1 | /** \file algorithm.cpp ******************************************************
2 | *
3 | * Project: MAXREFDES117#
4 | * Filename: algorithm.cpp
5 | * Description: This module calculates the heart rate/SpO2 level
6 | *
7 | *
8 | * --------------------------------------------------------------------
9 | *
10 | * This code follows the following naming conventions:
11 | *
12 | * char ch_pmod_value
13 | * char (array) s_pmod_s_string[16]
14 | * float f_pmod_value
15 | * int32_t n_pmod_value
16 | * int32_t (array) an_pmod_value[16]
17 | * int16_t w_pmod_value
18 | * int16_t (array) aw_pmod_value[16]
19 | * uint16_t uw_pmod_value
20 | * uint16_t (array) auw_pmod_value[16]
21 | * uint8_t uch_pmod_value
22 | * uint8_t (array) auch_pmod_buffer[16]
23 | * uint32_t un_pmod_value
24 | * int32_t * pn_pmod_value
25 | *
26 | * ------------------------------------------------------------------------- */
27 | /*******************************************************************************
28 | * Copyright (C) 2016 Maxim Integrated Products, Inc., All Rights Reserved.
29 | *
30 | * Permission is hereby granted, free of charge, to any person obtaining a
31 | * copy of this software and associated documentation files (the "Software"),
32 | * to deal in the Software without restriction, including without limitation
33 | * the rights to use, copy, modify, merge, publish, distribute, sublicense,
34 | * and/or sell copies of the Software, and to permit persons to whom the
35 | * Software is furnished to do so, subject to the following conditions:
36 | *
37 | * The above copyright notice and this permission notice shall be included
38 | * in all copies or substantial portions of the Software.
39 | *
40 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
41 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
42 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
43 | * IN NO EVENT SHALL MAXIM INTEGRATED BE LIABLE FOR ANY CLAIM, DAMAGES
44 | * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
45 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
46 | * OTHER DEALINGS IN THE SOFTWARE.
47 | *
48 | * Except as contained in this notice, the name of Maxim Integrated
49 | * Products, Inc. shall not be used except as stated in the Maxim Integrated
50 | * Products, Inc. Branding Policy.
51 | *
52 | * The mere transfer of this software does not imply any licenses
53 | * of trade secrets, proprietary technology, copyrights, patents,
54 | * trademarks, maskwork rights, or any other form of intellectual
55 | * property whatsoever. Maxim Integrated Products, Inc. retains all
56 | * ownership rights.
57 | *******************************************************************************
58 | */
59 |
60 | #include "Arduino.h"
61 | #include "spo2_algorithm.h"
62 |
63 | #if defined(ARDUINO_AVR_UNO)
64 | //Arduino Uno doesn't have enough SRAM to store 100 samples of IR led data and red led data in 32-bit format
65 | //To solve this problem, 16-bit MSB of the sampled data will be truncated. Samples become 16-bit data.
66 | void maxim_heart_rate_and_oxygen_saturation(uint16_t *pun_ir_buffer, int32_t n_ir_buffer_length, uint16_t *pun_red_buffer, int32_t *pn_spo2, int8_t *pch_spo2_valid,
67 | int32_t *pn_heart_rate, int8_t *pch_hr_valid)
68 | #else
69 | void maxim_heart_rate_and_oxygen_saturation(uint32_t *pun_ir_buffer, int32_t n_ir_buffer_length, uint32_t *pun_red_buffer, int32_t *pn_spo2, int8_t *pch_spo2_valid,
70 | int32_t *pn_heart_rate, int8_t *pch_hr_valid)
71 | #endif
72 | /**
73 | * \brief Calculate the heart rate and SpO2 level
74 | * \par Details
75 | * By detecting peaks of PPG cycle and corresponding AC/DC of red/infra-red signal, the an_ratio for the SPO2 is computed.
76 | * Since this algorithm is aiming for Arm M0/M3. formaula for SPO2 did not achieve the accuracy due to register overflow.
77 | * Thus, accurate SPO2 is precalculated and save longo uch_spo2_table[] per each an_ratio.
78 | *
79 | * \param[in] *pun_ir_buffer - IR sensor data buffer
80 | * \param[in] n_ir_buffer_length - IR sensor data buffer length
81 | * \param[in] *pun_red_buffer - Red sensor data buffer
82 | * \param[out] *pn_spo2 - Calculated SpO2 value
83 | * \param[out] *pch_spo2_valid - 1 if the calculated SpO2 value is valid
84 | * \param[out] *pn_heart_rate - Calculated heart rate value
85 | * \param[out] *pch_hr_valid - 1 if the calculated heart rate value is valid
86 | *
87 | * \retval None
88 | */
89 | {
90 | uint32_t un_ir_mean,un_only_once ;
91 | int32_t k, n_i_ratio_count;
92 | int32_t i, s, m, n_exact_ir_valley_locs_count, n_middle_idx;
93 | int32_t n_th1, n_npks, n_c_min;
94 | int32_t an_ir_valley_locs[15] ;
95 | int32_t n_peak_interval_sum;
96 |
97 | int32_t n_y_ac, n_x_ac;
98 | int32_t n_spo2_calc;
99 | int32_t n_y_dc_max, n_x_dc_max;
100 | int32_t n_y_dc_max_idx, n_x_dc_max_idx;
101 | int32_t an_ratio[5], n_ratio_average;
102 | int32_t n_nume, n_denom ;
103 |
104 | // calculates DC mean and subtract DC from ir
105 | un_ir_mean =0;
106 | for (k=0 ; k60) n_th1=60; // max allowed
125 |
126 | for ( k=0 ; k<15;k++) an_ir_valley_locs[k]=0;
127 | // since we flipped signal, we use peak detector as valley detector
128 | maxim_find_peaks( an_ir_valley_locs, &n_npks, an_x, BUFFER_SIZE, n_th1, 4, 15 );//peak_height, peak_distance, max_num_peaks
129 | n_peak_interval_sum =0;
130 | if (n_npks>=2){
131 | for (k=1; k BUFFER_SIZE ){
158 | *pn_spo2 = -999 ; // do not use SPO2 since valley loc is out of range
159 | *pch_spo2_valid = 0;
160 | return;
161 | }
162 | }
163 | // find max between two valley locations
164 | // and use an_ratio betwen AC compoent of Ir & Red and DC compoent of Ir & Red for SPO2
165 | for (k=0; k< n_exact_ir_valley_locs_count-1; k++){
166 | n_y_dc_max= -16777216 ;
167 | n_x_dc_max= -16777216;
168 | if (an_ir_valley_locs[k+1]-an_ir_valley_locs[k] >3){
169 | for (i=an_ir_valley_locs[k]; i< an_ir_valley_locs[k+1]; i++){
170 | if (an_x[i]> n_x_dc_max) {n_x_dc_max =an_x[i]; n_x_dc_max_idx=i;}
171 | if (an_y[i]> n_y_dc_max) {n_y_dc_max =an_y[i]; n_y_dc_max_idx=i;}
172 | }
173 | n_y_ac= (an_y[an_ir_valley_locs[k+1]] - an_y[an_ir_valley_locs[k] ] )*(n_y_dc_max_idx -an_ir_valley_locs[k]); //red
174 | n_y_ac= an_y[an_ir_valley_locs[k]] + n_y_ac/ (an_ir_valley_locs[k+1] - an_ir_valley_locs[k]) ;
175 | n_y_ac= an_y[n_y_dc_max_idx] - n_y_ac; // subracting linear DC compoenents from raw
176 | n_x_ac= (an_x[an_ir_valley_locs[k+1]] - an_x[an_ir_valley_locs[k] ] )*(n_x_dc_max_idx -an_ir_valley_locs[k]); // ir
177 | n_x_ac= an_x[an_ir_valley_locs[k]] + n_x_ac/ (an_ir_valley_locs[k+1] - an_ir_valley_locs[k]);
178 | n_x_ac= an_x[n_y_dc_max_idx] - n_x_ac; // subracting linear DC compoenents from raw
179 | n_nume=( n_y_ac *n_x_dc_max)>>7 ; //prepare X100 to preserve floating value
180 | n_denom= ( n_x_ac *n_y_dc_max)>>7;
181 | if (n_denom>0 && n_i_ratio_count <5 && n_nume != 0)
182 | {
183 | an_ratio[n_i_ratio_count]= (n_nume*100)/n_denom ; //formular is ( n_y_ac *n_x_dc_max) / ( n_x_ac *n_y_dc_max) ;
184 | n_i_ratio_count++;
185 | }
186 | }
187 | }
188 | // choose median value since PPG signal may varies from beat to beat
189 | maxim_sort_ascend(an_ratio, n_i_ratio_count);
190 | n_middle_idx= n_i_ratio_count/2;
191 |
192 | if (n_middle_idx >1)
193 | n_ratio_average =( an_ratio[n_middle_idx-1] +an_ratio[n_middle_idx])/2; // use median
194 | else
195 | n_ratio_average = an_ratio[n_middle_idx ];
196 |
197 | if( n_ratio_average>2 && n_ratio_average <184){
198 | n_spo2_calc= uch_spo2_table[n_ratio_average] ;
199 | *pn_spo2 = n_spo2_calc ;
200 | *pch_spo2_valid = 1;// float_SPO2 = -45.060*n_ratio_average* n_ratio_average/10000 + 30.354 *n_ratio_average/100 + 94.845 ; // for comparison with table
201 | }
202 | else{
203 | *pn_spo2 = -999 ; // do not use SPO2 since signal an_ratio is out of range
204 | *pch_spo2_valid = 0;
205 | }
206 | }
207 |
208 |
209 | void maxim_find_peaks( int32_t *pn_locs, int32_t *n_npks, int32_t *pn_x, int32_t n_size, int32_t n_min_height, int32_t n_min_distance, int32_t n_max_num )
210 | /**
211 | * \brief Find peaks
212 | * \par Details
213 | * Find at most MAX_NUM peaks above MIN_HEIGHT separated by at least MIN_DISTANCE
214 | *
215 | * \retval None
216 | */
217 | {
218 | maxim_peaks_above_min_height( pn_locs, n_npks, pn_x, n_size, n_min_height );
219 | maxim_remove_close_peaks( pn_locs, n_npks, pn_x, n_min_distance );
220 | *n_npks = min( *n_npks, n_max_num );
221 | }
222 |
223 | void maxim_peaks_above_min_height( int32_t *pn_locs, int32_t *n_npks, int32_t *pn_x, int32_t n_size, int32_t n_min_height )
224 | /**
225 | * \brief Find peaks above n_min_height
226 | * \par Details
227 | * Find all peaks above MIN_HEIGHT
228 | *
229 | * \retval None
230 | */
231 | {
232 | int32_t i = 1, n_width;
233 | *n_npks = 0;
234 |
235 | while (i < n_size-1){
236 | if (pn_x[i] > n_min_height && pn_x[i] > pn_x[i-1]){ // find left edge of potential peaks
237 | n_width = 1;
238 | while (i+n_width < n_size && pn_x[i] == pn_x[i+n_width]) // find flat peaks
239 | n_width++;
240 | if (pn_x[i] > pn_x[i+n_width] && (*n_npks) < 15 ){ // find right edge of peaks
241 | pn_locs[(*n_npks)++] = i;
242 | // for flat peaks, peak location is left edge
243 | i += n_width+1;
244 | }
245 | else
246 | i += n_width;
247 | }
248 | else
249 | i++;
250 | }
251 | }
252 |
253 | void maxim_remove_close_peaks(int32_t *pn_locs, int32_t *pn_npks, int32_t *pn_x, int32_t n_min_distance)
254 | /**
255 | * \brief Remove peaks
256 | * \par Details
257 | * Remove peaks separated by less than MIN_DISTANCE
258 | *
259 | * \retval None
260 | */
261 | {
262 |
263 | int32_t i, j, n_old_npks, n_dist;
264 |
265 | /* Order peaks from large to small */
266 | maxim_sort_indices_descend( pn_x, pn_locs, *pn_npks );
267 |
268 | for ( i = -1; i < *pn_npks; i++ ){
269 | n_old_npks = *pn_npks;
270 | *pn_npks = i+1;
271 | for ( j = i+1; j < n_old_npks; j++ ){
272 | n_dist = pn_locs[j] - ( i == -1 ? -1 : pn_locs[i] ); // lag-zero peak of autocorr is at index -1
273 | if ( n_dist > n_min_distance || n_dist < -n_min_distance )
274 | pn_locs[(*pn_npks)++] = pn_locs[j];
275 | }
276 | }
277 |
278 | // Resort indices int32_to ascending order
279 | maxim_sort_ascend( pn_locs, *pn_npks );
280 | }
281 |
282 | void maxim_sort_ascend(int32_t *pn_x, int32_t n_size)
283 | /**
284 | * \brief Sort array
285 | * \par Details
286 | * Sort array in ascending order (insertion sort algorithm)
287 | *
288 | * \retval None
289 | */
290 | {
291 | int32_t i, j, n_temp;
292 | for (i = 1; i < n_size; i++) {
293 | n_temp = pn_x[i];
294 | for (j = i; j > 0 && n_temp < pn_x[j-1]; j--)
295 | pn_x[j] = pn_x[j-1];
296 | pn_x[j] = n_temp;
297 | }
298 | }
299 |
300 | void maxim_sort_indices_descend( int32_t *pn_x, int32_t *pn_indx, int32_t n_size)
301 | /**
302 | * \brief Sort indices
303 | * \par Details
304 | * Sort indices according to descending order (insertion sort algorithm)
305 | *
306 | * \retval None
307 | */
308 | {
309 | int32_t i, j, n_temp;
310 | for (i = 1; i < n_size; i++) {
311 | n_temp = pn_indx[i];
312 | for (j = i; j > 0 && pn_x[n_temp] > pn_x[pn_indx[j-1]]; j--)
313 | pn_indx[j] = pn_indx[j-1];
314 | pn_indx[j] = n_temp;
315 | }
316 | }
317 |
318 |
319 |
320 |
321 |
--------------------------------------------------------------------------------
/Libraries/Arduino/src/spo2_algorithm.h:
--------------------------------------------------------------------------------
1 | /** \file algorithm.h ******************************************************
2 | *
3 | * Project: MAXREFDES117#
4 | * Filename: algorithm.h
5 | * Description: This module is the heart rate/SpO2 calculation algorithm header file
6 | *
7 | * Revision History:
8 | *\n 1-18-2016 Rev 01.00 SK Initial release.
9 | *\n
10 | *
11 | * --------------------------------------------------------------------
12 | *
13 | * This code follows the following naming conventions:
14 | *
15 | *\n char ch_pmod_value
16 | *\n char (array) s_pmod_s_string[16]
17 | *\n float f_pmod_value
18 | *\n int32_t n_pmod_value
19 | *\n int32_t (array) an_pmod_value[16]
20 | *\n int16_t w_pmod_value
21 | *\n int16_t (array) aw_pmod_value[16]
22 | *\n uint16_t uw_pmod_value
23 | *\n uint16_t (array) auw_pmod_value[16]
24 | *\n uint8_t uch_pmod_value
25 | *\n uint8_t (array) auch_pmod_buffer[16]
26 | *\n uint32_t un_pmod_value
27 | *\n int32_t * pn_pmod_value
28 | *
29 | * ------------------------------------------------------------------------- */
30 | /*******************************************************************************
31 | * Copyright (C) 2015 Maxim Integrated Products, Inc., All Rights Reserved.
32 | *
33 | * Permission is hereby granted, free of charge, to any person obtaining a
34 | * copy of this software and associated documentation files (the "Software"),
35 | * to deal in the Software without restriction, including without limitation
36 | * the rights to use, copy, modify, merge, publish, distribute, sublicense,
37 | * and/or sell copies of the Software, and to permit persons to whom the
38 | * Software is furnished to do so, subject to the following conditions:
39 | *
40 | * The above copyright notice and this permission notice shall be included
41 | * in all copies or substantial portions of the Software.
42 | *
43 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
44 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
45 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
46 | * IN NO EVENT SHALL MAXIM INTEGRATED BE LIABLE FOR ANY CLAIM, DAMAGES
47 | * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
48 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
49 | * OTHER DEALINGS IN THE SOFTWARE.
50 | *
51 | * Except as contained in this notice, the name of Maxim Integrated
52 | * Products, Inc. shall not be used except as stated in the Maxim Integrated
53 | * Products, Inc. Branding Policy.
54 | *
55 | * The mere transfer of this software does not imply any licenses
56 | * of trade secrets, proprietary technology, copyrights, patents,
57 | * trademarks, maskwork rights, or any other form of intellectual
58 | * property whatsoever. Maxim Integrated Products, Inc. retains all
59 | * ownership rights.
60 | *******************************************************************************
61 | */
62 | #ifndef SPO2_ALGORITHM_H_
63 | #define SPO2_ALGORITHM_H_
64 |
65 | #include
66 |
67 | #define FS 25 //sampling frequency
68 | #define BUFFER_SIZE (FS * 4)
69 | #define MA4_SIZE 4 // DONOT CHANGE
70 | #define min(x,y) ((x) < (y) ? (x) : (y))
71 |
72 | //uch_spo2_table is approximated as -45.060*ratioAverage* ratioAverage + 30.354 *ratioAverage + 94.845 ;
73 | const uint8_t uch_spo2_table[184]={ 95, 95, 95, 96, 96, 96, 97, 97, 97, 97, 97, 98, 98, 98, 98, 98, 99, 99, 99, 99,
74 | 99, 99, 99, 99, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100,
75 | 100, 100, 100, 100, 99, 99, 99, 99, 99, 99, 99, 99, 98, 98, 98, 98, 98, 98, 97, 97,
76 | 97, 97, 96, 96, 96, 96, 95, 95, 95, 94, 94, 94, 93, 93, 93, 92, 92, 92, 91, 91,
77 | 90, 90, 89, 89, 89, 88, 88, 87, 87, 86, 86, 85, 85, 84, 84, 83, 82, 82, 81, 81,
78 | 80, 80, 79, 78, 78, 77, 76, 76, 75, 74, 74, 73, 72, 72, 71, 70, 69, 69, 68, 67,
79 | 66, 66, 65, 64, 63, 62, 62, 61, 60, 59, 58, 57, 56, 56, 55, 54, 53, 52, 51, 50,
80 | 49, 48, 47, 46, 45, 44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 31, 30, 29,
81 | 28, 27, 26, 25, 23, 22, 21, 20, 19, 17, 16, 15, 14, 12, 11, 10, 9, 7, 6, 5,
82 | 3, 2, 1 } ;
83 | static int32_t an_x[ BUFFER_SIZE]; //ir
84 | static int32_t an_y[ BUFFER_SIZE]; //red
85 |
86 |
87 | #if defined(ARDUINO_AVR_UNO)
88 | //Arduino Uno doesn't have enough SRAM to store 100 samples of IR led data and red led data in 32-bit format
89 | //To solve this problem, 16-bit MSB of the sampled data will be truncated. Samples become 16-bit data.
90 | void maxim_heart_rate_and_oxygen_saturation(uint16_t *pun_ir_buffer, int32_t n_ir_buffer_length, uint16_t *pun_red_buffer, int32_t *pn_spo2, int8_t *pch_spo2_valid, int32_t *pn_heart_rate, int8_t *pch_hr_valid);
91 | #else
92 | void maxim_heart_rate_and_oxygen_saturation(uint32_t *pun_ir_buffer, int32_t n_ir_buffer_length, uint32_t *pun_red_buffer, int32_t *pn_spo2, int8_t *pch_spo2_valid, int32_t *pn_heart_rate, int8_t *pch_hr_valid);
93 | #endif
94 |
95 | void maxim_find_peaks(int32_t *pn_locs, int32_t *n_npks, int32_t *pn_x, int32_t n_size, int32_t n_min_height, int32_t n_min_distance, int32_t n_max_num);
96 | void maxim_peaks_above_min_height(int32_t *pn_locs, int32_t *n_npks, int32_t *pn_x, int32_t n_size, int32_t n_min_height);
97 | void maxim_remove_close_peaks(int32_t *pn_locs, int32_t *pn_npks, int32_t *pn_x, int32_t n_min_distance);
98 | void maxim_sort_ascend(int32_t *pn_x, int32_t n_size);
99 | void maxim_sort_indices_descend(int32_t *pn_x, int32_t *pn_indx, int32_t n_size);
100 |
101 | #endif /* ALGORITHM_H_ */
102 |
103 |
--------------------------------------------------------------------------------
/MAX30105-Sensor-Layout-1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sparkfun/MAX30105_Particle_Sensor_Breakout/1c5c88c8b99ae6edd1ba226c16e37b433641d56e/MAX30105-Sensor-Layout-1.jpg
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | MAX30105 Particle and Pulse Ox Sensor Breakout
2 | =======
3 |
4 | 
5 |
6 | A breakout board for the MAX30105 Particle Sensor. This sensor uses three LEDs (Red, Green, IR) to shine different wavelengths of light at any particle in front of the photo detector. Based on the reflection the sensor outputs readings.
7 |
8 | 
9 |
10 | That's my heart beat!
11 |
12 | Sensor is capable of detecting pulses but only limited algorithms are provided. It is up to the user to create the algorithms to detect different particles and/or SPO2 and/or do Pulse Detection.
13 |
14 | Board requires 3.3V to operate the IR and Red LEDs. 3.5V is recommended to operate the green LED. It has onboard voltage regulation and BSS138 MOSFETs to convert from larger logic voltages (5V) down to the needed 1.8V.
15 |
16 | License Information
17 | -------------------
18 |
19 | The hardware is released under [Creative Commons Share-alike 4.0](http://creativecommons.org/licenses/by-sa/4.0/).
20 | The firmware is released under the [Beerware license](http://en.wikipedia.org/wiki/Beerware).
--------------------------------------------------------------------------------