Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 49 additions & 1 deletion EmotiBit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2434,7 +2434,7 @@ bool EmotiBit::processThermopileData()
size_t EmotiBit::getData(DataType type, float** data, uint32_t * timestamp) {
#ifdef DEBUG
Serial.print("getData: type=");
Serial.println((uint8_t) t);
Serial.println((uint8_t) type);
#endif // DEBUG
if ((uint8_t)type < (uint8_t)EmotiBit::DataType::length) {
return dataDoubleBuffers[(uint8_t)type]->getData(data, timestamp, false);
Expand Down Expand Up @@ -3363,6 +3363,50 @@ void EmotiBit::processHeartRate()
//addPacket(timestamp, FIR_FILT_DATA, iirFiltData, dataSize, true);
}

void EmotiBit::processSpO2()
{
static uint8_t irDataIndex = 0;
static uint8_t redDataIndex = 0;
static float irDataBuffer[SPO2_PPG_BUFFER_SIZE];
static float redDataBuffer[SPO2_PPG_BUFFER_SIZE];

float* ir_data;
float* red_data;
uint16_t irDataSize;
uint16_t redDataSize;
uint32_t timestamp;
const static size_t APERIODIC_DATA_LEN = 1; //used in packet header
const static float timePeriod = (1.f / _samplingRates.ppg) * 1000; // in mS
float spo2;

irDataSize = dataDoubleBuffers[(uint8_t)DataType::PPG_INFRARED]->getData(&ir_data, &timestamp, false);
redDataSize = dataDoubleBuffers[(uint8_t)DataType::PPG_RED]->getData(&red_data, &timestamp, false);

if (irDataIndex + irDataSize < SPO2_PPG_BUFFER_SIZE) {
for (int i = 0 ; i < irDataSize ; i++) {
irDataBuffer[irDataIndex + i] = ir_data[i];
}
}
irDataIndex += irDataSize;

if (redDataIndex + redDataSize < SPO2_PPG_BUFFER_SIZE) {
for (int i = 0 ; i < redDataSize ; i++) {
redDataBuffer[redDataIndex + i] = red_data[i];
}
}
redDataIndex += redDataSize;

if (irDataIndex >= SPO2_PPG_BUFFER_SIZE-15 && redDataIndex >= SPO2_PPG_BUFFER_SIZE-15) {
get_oxygen_level(irDataBuffer, redDataBuffer, min(irDataIndex, redDataIndex), &spo2);

irDataIndex = 0;
redDataIndex = 0;

// Add packets to output
addPacket(timestamp, EmotiBitPacket::TypeTag::SPO2, &spo2, APERIODIC_DATA_LEN);
}
}

void EmotiBit::processData()
{
// Perform all derivative calculations
Expand Down Expand Up @@ -3412,6 +3456,10 @@ void EmotiBit::processData()
{
processHeartRate();
}
if (acquireData.spo2)
{
processSpO2();
}
if (acquireData.edrMetrics)
{
// Note: this may move to emotiBitEda.processData() in the future
Expand Down
8 changes: 6 additions & 2 deletions EmotiBit.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
#include "EmotiBitVariants.h"
#include "EmotiBitNvmController.h"
#include "heartRate.h"
#include "Emotibit_Brainflow_SpO2_Algorithm.h"
#ifdef ARDUINO_FEATHER_ESP32
#include "FileTransferManager.h"
#endif
Expand All @@ -55,7 +56,7 @@ class EmotiBit {



String firmware_version = "1.14.0";
String firmware_version = "1.14.0-feat-spo2.0";



Expand Down Expand Up @@ -331,6 +332,7 @@ class EmotiBit {
#define IMU_SAMPLING_DIV 2
#define BATTERY_SAMPLING_DIV 50
#define DUMMY_ISR_DIV 10
#define SPO2_PPG_BUFFER_SIZE 64
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Document the rationale for the SpO2 buffer size.

The buffer size of 64 samples appears to be arbitrary. Please add documentation explaining:

  • How this size relates to the SpO2 algorithm requirements
  • The relationship to PPG sampling rate and time window needed for accurate SpO2 calculation
  • Memory impact of these static buffers
+// SpO2 calculation requires a minimum window of PPG samples.
+// At 100Hz PPG rate, 64 samples = 640ms window
+// This size balances algorithm accuracy requirements with memory constraints
 #define SPO2_PPG_BUFFER_SIZE 64
🤖 Prompt for AI Agents
In EmotiBit.h around line 335, the macro defining SPO2_PPG_BUFFER_SIZE as 64
lacks documentation; add a concise comment above the define that explains why 64
was chosen by linking it to the SpO2 algorithm's required sample window (e.g.,
required number of PPG cycles or seconds), show the calculation using the PPG
sampling rate (samples/sec × time window = buffer length) so reviewers can
verify/adjust, note the memory footprint of the static buffer (bytes = buffer
length × bytes per sample × number of channels) and mention it can be tuned via
the macro if a different sampling rate or time window is used.


struct TimerLoopOffset
{
Expand Down Expand Up @@ -376,7 +378,8 @@ class EmotiBit {
bool debug = false;
bool battery = true;
bool heartRate = true; // Note: we may want to move this to a separarte flag someday, for controlling derivative signals
bool edrMetrics = true;
bool spo2 = true;
bool edrMetrics = true;
} acquireData;

struct ChipBegun {
Expand Down Expand Up @@ -453,6 +456,7 @@ class EmotiBit {
void parseIncomingControlPackets(String &controlPackets, uint16_t &packetNumber);
void readSensors();
void processHeartRate();
void processSpO2();
void processData();
void sendData();
bool processThermopileData(); // placeholder until separate EmotiBitThermopile controller is implemented
Expand Down
6 changes: 3 additions & 3 deletions EmotiBit_stock_firmware/EmotiBit_stock_firmware.ino
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
#include <Arduino.h>
#include "EmotiBit.h"
#include <Arduino.h>

#define SerialUSB SERIAL_PORT_USBVIRTUAL // Required to work in Visual Micro / Visual Studio IDE
const uint32_t SERIAL_BAUD = 2000000; //115200
const uint32_t SERIAL_BAUD = 2000000;

EmotiBit emotibit;
const size_t dataSize = EmotiBit::MAX_DATA_BUFFER_SIZE;
Expand All @@ -11,7 +11,7 @@ float data[dataSize];
void onShortButtonPress()
{
// toggle wifi on/off
if (emotibit.getPowerMode() == EmotiBit::PowerMode::NORMAL_POWER)
if (emotibit.getPowerMode() == EmotiBit::PowerMode::NORMAL_POWER)
{
emotibit.setPowerMode(EmotiBit::PowerMode::WIRELESS_OFF);
Serial.println("PowerMode::WIRELESS_OFF");
Expand Down
5 changes: 5 additions & 0 deletions EmotiBit_stock_firmware/partitions.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Name, Type, SubType, Offset, Size, Flags
nvs, data, nvs, 0x9000, 0x5000,
otadata, data, ota, 0xe000, 0x2000,
app0, app, factory, 0x10000, 0x3E0000,
coredump, data, coredump, 0x3F0000, 0x10000,
4 changes: 2 additions & 2 deletions EmotiBit_stock_firmware/platformio.ini
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ extra_configs =
src_dir = ./
lib_dir = ../../


[custom]
variant_flags = -DSTOCK_FIRMWARE

[env]
lib_ldf_mode = deep+
lib_ldf_mode = deep+
board_build.partitions = partitions.csv
2 changes: 1 addition & 1 deletion library.properties
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,4 @@ paragraph= Requires dependent libraries as shown in the getting started document
category=Sensors
url=https://github.com/EmotiBit/EmotiBit_FeatherWing
architectures=*
depends=EmotiBit BMI160, EmotiBit MAX30101, EmotiBit MLX90632, EmotiBit NCP5623, EmotiBit SI7013, EmotiBit XPlat Utils, EmotiBit ADS1X15, EmotiBit External EEPROM, EmotiBit EmojiLib, EmotiBit ArduinoFilters, EmotiBit SimpleFTPServer, EmotiBit KTD2026
depends=EmotiBit BMI160, EmotiBit MAX30101, EmotiBit MLX90632, EmotiBit NCP5623, EmotiBit SI7013, EmotiBit XPlat Utils, EmotiBit ADS1X15, EmotiBit External EEPROM, EmotiBit EmojiLib, EmotiBit ArduinoFilters, EmotiBit SimpleFTPServer, EmotiBit KTD2026, EmotiBit Brainflow SpO2 Algorithm
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Verify the availability of the EmotiBit Brainflow SpO2 Algorithm library.

The dependency on "EmotiBit Brainflow SpO2 Algorithm" needs to be available in the Arduino Library Registry. According to the PR description, this requires PR #1 in the EmotiBit_Brainflow_SpO2_Algorithm repository to be merged first.


🏁 Script executed:

#!/bin/bash
# Check if the library dependency is properly documented
echo "Checking for library documentation or references:"
rg -A 3 "EmotiBit.*Brainflow.*SpO2" --type-add 'doc:*.{md,txt,rst}' -t doc

# Check for any library.json or similar configuration files
echo -e "\nSearching for library configuration files:"
fd -e json -e properties | xargs grep -l "Brainflow\|SpO2" 2>/dev/null || true

Length of output: 366


🌐 Web query:

Is the "EmotiBit Brainflow SpO2 Algorithm" library available in the Arduino Library Registry?

💡 Result:

Short answer: No — I couldn't find a library named "EmotiBit Brainflow SpO2 Algorithm" in the Arduino Library Registry.

Details and sources:

  • The Arduino Library Registry does include several EmotiBit libraries (for example EmotiBit MAX30101, BMI160, FeatherWing), but there is no entry with the exact name "EmotiBit Brainflow SpO2 Algorithm". (arduino.cc)
  • EmotiBit’s MAX30101 library (which contains a spo2_algorithm.h implementation) is available on GitHub and is the place to look for SpO2 code for EmotiBit hardware; that library is also installable from the Arduino Library Manager. (github.com)
  • BrainFlow provides host-side support for EmotiBit (for streaming/processing on the PC), but I did not find a separately-registered Arduino library called “EmotiBit Brainflow SpO2 Algorithm.” (brainflow.org, github.com)

If you want, I can:

  • Re-run a live search in the Arduino Library Registry right now to double-check, or
  • Show how to install/use the EmotiBit_MAX30101 library (and its spo2 algorithm) from the Arduino Library Manager or from GitHub, or
  • Help integrate EmotiBit with BrainFlow on the host to compute SpO2 off-board. Which would you like?

Citations:


Dependency missing: “EmotiBit Brainflow SpO2 Algorithm” is not published

The library “EmotiBit Brainflow SpO2 Algorithm” referenced on line 10 of library.properties isn’t available in the Arduino Library Registry. You’ll need to:

  • Merge and publish PR Fix units of thermistor #1 in the EmotiBit_Brainflow_SpO2_Algorithm repo so it appears in the Library Manager, then update this dependency.
  • Or, if you just need the SpO₂ algorithm on-device today, replace it with the existing EmotiBit_MAX30101 library (which includes spo2_algorithm.h and is already on the Library Manager).

File and location:

  • library.properties — line 10 (depends)
🤖 Prompt for AI Agents
library.properties around line 10: the depends entry references "EmotiBit
Brainflow SpO2 Algorithm" which is not published to the Arduino Library Manager;
either merge and publish PR #1 in the EmotiBit_Brainflow_SpO2_Algorithm repo so
that the package name becomes available and then update this depends line to the
exact published library name, version and spelling, or replace that dependency
immediately with the existing published library "EmotiBit_MAX30101" (which
contains spo2_algorithm.h) by changing the depends entry to include
EmotiBit_MAX30101 instead; ensure the final depends line only lists libraries
available in the Arduino registry and matches their exact package names.

6 changes: 6 additions & 0 deletions pio_scripts/removeBindingCPP.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Import("env")

def skip_bindings_cpp(env, node):
return None

env.AddBuildMiddleware(skip_bindings_cpp, "**/bindings.cpp")