Skip to content

Commit

Permalink
Add Feature
Browse files Browse the repository at this point in the history
Support for ESP32-S2
  • Loading branch information
biomurph authored Feb 1, 2023
1 parent 591f6e3 commit 473b0f5
Showing 1 changed file with 372 additions and 0 deletions.
372 changes: 372 additions & 0 deletions examples/PulseSensor_ESP32/PulseSensor_ESP32.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,372 @@
/*
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. Use that url in any browser
on the same network to connect and veiw the webpage.

Code to detect pulses from the PulseSensor,
using an interrupt service routine.

Here is a link to the tutorial\
https://pulsesensor.com/pages/getting-advanced

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>

/*
The following hardware timer setup supports ESP32
*/
hw_timer_t * sampleTimer = NULL;
portMUX_TYPE sampleTimerMux = portMUX_INITIALIZER_UNLOCKED;

/*
Every Sketch that uses the PulseSensor Playground must
define USE_ARDUINO_INTERRUPTS before including PulseSensorPlayground.h.
Here, #define USE_ARDUINO_INTERRUPTS true tells the library to use
interrupts to automatically read and process PulseSensor data.
*/
#define USE_ARDUINO_INTERRUPTS true
//#define NO_PULSE_SENSOR_SERIAL true
#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 resistor)
that will flash on each detected pulse.
PULSE_FADE = digital Output. PWM pin onnected to an LED (and 1K esistor)
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.print("\nPulseSensor Server url: ");
Serial.println(WiFi.localIP());
}


/*
This is the interrupt service routine.
We need to declare it after the PulseSensor Playground
library is compiled, so that the onSampleTime
function is known.
*/
void IRAM_ATTR onSampleTime() {
portENTER_CRITICAL_ISR(&sampleTimerMux);
PulseSensorPlayground::OurThis->onSampleTime();
portEXIT_CRITICAL_ISR(&sampleTimerMux);
}

/*
When sendPulseSignal is true, PulseSensor Signal data
is sent to the serial port for user monitoring.
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);
}
}

/*
This will set up and start the timer interrupt on ESP32.
The interrupt will occur every 2000uS or 500Hz.
*/
sampleTimer = timerBegin(0, 80, true);
timerAttachInterrupt(sampleTimer, &onSampleTime, true);
timerAlarmWrite(sampleTimer, 2000, true);
timerAlarmEnable(sampleTimer);

/*
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 */
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 '?':
printControlInfo();
break;
default:
break;
}
}
}

/*
This function prints the control information to the serial monitor
*/
void printControlInfo(){
Serial.println("PulseSensor ESP32 Example");
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");
}


0 comments on commit 473b0f5

Please sign in to comment.