From 473b0f56efed55b7df46b5732c34d8d07cfbc719 Mon Sep 17 00:00:00 2001 From: Joel Murphy Date: Wed, 1 Feb 2023 12:57:18 -0500 Subject: [PATCH] Add Feature Support for ESP32-S2 --- .../PulseSensor_ESP32/PulseSensor_ESP32.ino | 372 ++++++++++++++++++ 1 file changed, 372 insertions(+) create mode 100644 examples/PulseSensor_ESP32/PulseSensor_ESP32.ino diff --git a/examples/PulseSensor_ESP32/PulseSensor_ESP32.ino b/examples/PulseSensor_ESP32/PulseSensor_ESP32.ino new file mode 100644 index 0000000..18beb5f --- /dev/null +++ b/examples/PulseSensor_ESP32/PulseSensor_ESP32.ino @@ -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 +#include +#include +#include +#include + +/* + 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 + +/* + 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( + + + + + + + + +

PulseSensor Server

+

Heart Rate + + bpm +

+ + +)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"); +} + +