/
PulseSensor_ESP32.ino
344 lines (301 loc) · 9.41 KB
/
PulseSensor_ESP32.ino
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
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
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
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
/*
This example sketch is for use with the ESP32.
The code below will serve a web page on the local network
and will refresh the BPM with every heartbeat.
On startup, the ESP32 will send it's network address
over the serial port (for example 192.168.1.172).
Use that url in any browser on the same local network
to connect and veiw the webpage. This code will not
make the page available outside your local network.
Check out the PulseSensor Playground Tools for explaination
of all user functions and directives.
https://github.com/WorldFamousElectronics/PulseSensorPlayground/blob/master/resources/PulseSensor%20Playground%20Tools.md
Copyright World Famous Electronics LLC - see LICENSE
Contributors:
Joel Murphy, https://pulsesensor.com
Yury Gitman, https://pulsesensor.com
Bradford Needham, @bneedhamia, https://bluepapertech.com
Licensed under the MIT License, a copy of which
should have been included with this software.
This software is not intended for medical use.
*/
/*
The following libraries are necessary
for the asynchronous web server
*/
#include <Arduino.h>
#include <WiFi.h>
#include <AsyncTCP.h>
#include <ESPAsyncWebServer.h>
#include <Arduino_JSON.h>
/*
Include PulseSensor Playground library for all the good stuff!
*/
#include <PulseSensorPlayground.h>
/*
We use JSON to pass data from the Arduino sketch to the Javascript
*/
JSONVar pulseData;
/*
Declare an instance of PulseSensor to access
all the PulseSensor Playground functions.
*/
PulseSensorPlayground pulseSensor;
/*
Pinout:
PULSE_INPUT = Analog Input. Connected to the pulse sensor
purple (signal) wire.
PULSE_BLINK = digital Output. Connected to an LED (and 1K series resistor)
that will flash on each detected pulse.
PULSE_FADE = digital Output. PWM pin onnected to an LED (and 1K series resistor)
that will smoothly fade with each pulse.
NOTE: PULSE_FADE must be a pin that supports PWM. Do not use
pin 9 or 10, because those pins' PWM interferes with the sample timer.
THRESHOLD should be set higher than the PulseSensor signal idles
at when there is nothing touching it. The expected idle value
should be 512, which is 1/2 of the ADC range. To check the idle value
open a serial monitor and make note of the PulseSensor signal values
with nothing touching the sensor. THRESHOLD should be a value higher
than the range of idle noise by 25 to 50 or so. When the library
is finding heartbeats, the value is adjusted based on the pulse signal
waveform. THRESHOLD sets the default when there is no pulse present.
Adjust as neccesary.
*/
const int PULSE_INPUT = A0;
const int PULSE_BLINK = 13;
const int PULSE_FADE = 5;
const int THRESHOLD = 685;
/* Replace with your network credentials */
const char* ssid = "SSID";
const char* password = "PASSWORD";
/*
Create AsyncWebServer object on port 80
Create an Event Source on /events
*/
AsyncWebServer server(80);
AsyncEventSource events("/events");
/*
The following code between the two "rawliteral" tags
will be stored as text. It contains the html,
css, and javascript that will be used to build
the asynchronous server.
*/
const char index_html[] PROGMEM = R"rawliteral(
<!DOCTYPE HTML html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" href="data:,">
<style>
html {
font-family: Arial;
display: inline-block;
margin: 0px auto;
text-align: center;
}
h2 { font-size: 3.0rem; }
p { font-size: 3.0rem; }
.reading {
font-size: 2.0rem;
color:black;
}
.dataType {
font-size: 1.8rem;
}
</style>
</head>
<body>
<h2>PulseSensor Server</h2>
<p
<span class="reading"> Heart Rate</span>
<span id="bpm"></span>
<span class="dataType">bpm</span>
</p>
</body>
<script>
window.addEventListener('load', getData);
function getData(){
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
var Jobj = JSON.parse(this.responseText);
console.log(Jobj);
document.getElementById("bpm").innerHTML = Jobj.heartrate;
}
};
xhr.open("GET", "/data", true);
xhr.send();
}
if (!!window.EventSource) {
var source = new EventSource('/events');
source.addEventListener('open', function(e) {
console.log("Events Connection");
}, false);
source.addEventListener('error', function(e) {
if (e.target.readyState != EventSource.OPEN) {
console.log("Events Disconnection");
}
}, false);
source.addEventListener('new_data', function(e) {
console.log("new_data", e.data);
var Jobj = JSON.parse(e.data);
document.getElementById("bpm").innerHTML = Jobj.heartrate;
}, false);
}
</script>
</html>)rawliteral";
/* Package the BPM in a JSON object */
String updatePulseDataJson(){
pulseData["heartrate"] = String(pulseSensor.getBeatsPerMinute());
String jsonString = JSON.stringify(pulseData);
return jsonString;
}
/*
Begin the WiFi and print the server url
to the serial port on connection
*/
void beginWiFi() {
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
Serial.print("Attempting to connect to ");
Serial.print(ssid);
while (WiFi.status() != WL_CONNECTED) {
Serial.print(" ~");
delay(1000);
}
Serial.println("\nConnected");
}
/*
When sendPulseSignal is true, PulseSensor Signal data
is sent to the serial port for user monitoring.
Modified by keys received on the Serial port.
Use the Serial Plotter to view the PulseSensor Signal wave.
*/
boolean sendPulseSignal = false;
void setup() {
/*
115200 baud provides about 11 bytes per millisecond.
The delay allows the port to settle so that
we don't miss out on info about the server url
in the Serial Monitor so we can connect a browser.
*/
Serial.begin(115200);
delay(1500);
beginWiFi();
/*
ESP32 analogRead defaults to 13 bit resolution
PulseSensor Playground library works with 10 bit
*/
analogReadResolution(10);
/* Configure the PulseSensor manager */
pulseSensor.analogInput(PULSE_INPUT);
pulseSensor.blinkOnPulse(PULSE_BLINK);
pulseSensor.fadeOnPulse(PULSE_FADE);
pulseSensor.setSerial(Serial);
pulseSensor.setThreshold(THRESHOLD);
/* Now that everything is ready, start reading the PulseSensor signal. */
if (!pulseSensor.begin()) {
while(1) {
/* If the pulseSensor object fails, flash the led */
digitalWrite(PULSE_BLINK, LOW);
delay(50);
digitalWrite(PULSE_BLINK, HIGH);
delay(50);
}
}
/*
When the server gets a request for the root url
serve the html
*/
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
request->send(200, "text/html", index_html);
});
/* Request for the latest PulseSensor data */
server.on("/data", HTTP_GET, [](AsyncWebServerRequest *request) {
String json = updatePulseDataJson();
request->send(200, "application/json", json);
json = String();
});
/*
Handler for when a client connects to the server
Only send serial feedback when NOT sending PulseSensor Signal data
Send event with short message and set reconnect timer to 2 seconds
*/
events.onConnect([](AsyncEventSourceClient *client) {
if(!sendPulseSignal){
if(client->lastId()){
Serial.println("Client Reconnected");
} else {
Serial.println("New Client Connected");
}
}
client->send("hello", NULL, millis(), 20000);
});
/* Create a handler for events */
server.addHandler(&events);
/* Start the server */
server.begin();
/* Print the control information to the serial monitor */
printControlInfo();
}
void loop() {
/*
Option to send the PulseSensor Signal data
to serial port for verification
*/
if(sendPulseSignal){
delay(20);
Serial.println(pulseSensor.getLatestSample());
}
/*
If a beat has happened since we last checked,
update the json data file to the server.
Also, send the new BPM value to the serial port
if we are not monitoring the pulse signal.
*/
if (pulseSensor.sawStartOfBeat()) {
events.send(updatePulseDataJson().c_str(),"new_data" ,millis());
if(!sendPulseSignal){
Serial.print(pulseSensor.getBeatsPerMinute());
Serial.println(" bpm");
}
}
/* Check to see if there are any commands sent to us */
serialCheck();
}
/*
This function checks to see if there are any commands available
on the Serial port. When you send keyboard characters 'b' or 'x'
you can turn on and off the signal data stream.
*/
void serialCheck(){
if(Serial.available() > 0){
char inChar = Serial.read();
switch(inChar){
case 'b':
sendPulseSignal = true;
break;
case 'x':
sendPulseSignal = false;
break;
case '?':
if(!printControlInfo){
printControlInfo();
}
break;
default:
break;
}
}
}
/*
This function prints the control information to the serial monitor
*/
void printControlInfo(){
Serial.println("PulseSensor ESP32 Example");
Serial.print("\nPulseSensor Server url: ");
Serial.println(WiFi.localIP());
Serial.println("Send 'b' to begin sending PulseSensor signal data");
Serial.println("Send 'x' to stop sendin PulseSensor signal data");
Serial.println("Send '?' to print this message");
}