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
157 changes: 118 additions & 39 deletions EmotiBit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,9 @@

EmotiBit* myEmotiBit = nullptr;
void(*onInterruptCallback)(void);

#ifdef ARDUINO_FEATHER_ESP32
TaskHandle_t EmotiBitDataAcquisition;
hw_timer_t * timer = NULL;
SemaphoreHandle_t _xMutex; ///< Semaphore to prevent concurrent access of double buffer between acquisition and update tasks
#endif

EmotiBit::EmotiBit()
Expand Down Expand Up @@ -1098,20 +1097,9 @@ uint8_t EmotiBit::setup(String firmwareVariant)
//Serial.println("Starting interrupts");
startTimer(BASE_SAMPLING_FREQ);
#elif defined ARDUINO_FEATHER_ESP32
// setup timer
timer = timerBegin(0, 80, true); // timer ticks with APB timer, which runs at 80MHz. This setting makes the timer tick every 1uS

// Attach onTimer function to our timer.
timerAttachInterrupt(timer, &onTimer, true);

// Set alarm to call onTimer function (value in microseconds).
// Repeat the alarm (third parameter)
timerAlarmWrite(timer, 1000000 / BASE_SAMPLING_FREQ, true);

// Start an alarm
timerAlarmEnable(timer);

attachToCore(&ReadSensors, this);
_xMutex = xSemaphoreCreateMutex();
attachReadSensorsToCore(&ReadSensors, this);
attachUpdateToCore(&Update);
#endif
}

Expand Down Expand Up @@ -1597,6 +1585,7 @@ void EmotiBit::updateButtonPress()

uint8_t EmotiBit::update()
{

if (testingMode == TestingMode::FACTORY_TEST)
{
processFactoryTestMessages();
Expand Down Expand Up @@ -1665,14 +1654,15 @@ uint8_t EmotiBit::update()
// NOTE:: An older firmware, v1.2.86 needs to be used to write EDA correction values to V3 (OTP) and below.
// Newer FW version can read both NVMs (EEPROM and OTP), but only have write ability for EEPROM


// Handle data buffer reading and sending
static uint32_t dataSendTimer = millis();
if (millis() - dataSendTimer > DATA_SEND_INTERVAL)
{
if (myEmotiBit->DIGITAL_WRITE_DEBUG) digitalWrite(myEmotiBit->DEBUG_OUT_PIN_1, HIGH);
_freeToSleep = false; // Sending data involves writing to storage. Dont sleep cores when writing to storage.
dataSendTimer = millis();




if (_sendTestData)
{
Expand All @@ -1698,6 +1688,9 @@ uint8_t EmotiBit::update()
bufferOverflowTest();
}
}

_freeToSleep = true;
if (myEmotiBit->DIGITAL_WRITE_DEBUG) digitalWrite(myEmotiBit->DEBUG_OUT_PIN_1, LOW);
}

// Hibernate after writing data
Expand Down Expand Up @@ -1842,7 +1835,7 @@ int8_t EmotiBit::updateThermopileData() {
#ifdef DEBUG_THERM_UPDATE
Serial.println("updateThermopileData");
#endif
if (DIGITAL_WRITE_DEBUG) digitalWrite(DEBUG_OUT_PIN_1, HIGH);

// Thermopile
int8_t status = 0;
uint32_t timestamp;
Expand Down Expand Up @@ -1871,12 +1864,11 @@ int8_t EmotiBit::updateThermopileData() {
#endif
if (thermStatus == MLX90632::status::SENSOR_SUCCESS)
{
if (DIGITAL_WRITE_DEBUG) digitalWrite(DEBUG_OUT_PIN_2, HIGH);

timestamp = millis();
status = status | therm0AMB.push_back(AMB, &(timestamp));
status = status | therm0Sto.push_back(Sto, &(timestamp));
thermopile.startRawSensorValues(thermStatus);
if (DIGITAL_WRITE_DEBUG) digitalWrite(DEBUG_OUT_PIN_2, LOW);
}
else
{
Expand All @@ -1897,7 +1889,7 @@ int8_t EmotiBit::updateThermopileData() {
}

_thermReadFinishedTime = micros();
if (DIGITAL_WRITE_DEBUG) digitalWrite(DEBUG_OUT_PIN_1, LOW);

return status;
}

Expand Down Expand Up @@ -2234,8 +2226,15 @@ bool EmotiBit::processThermopileData()

// Swap buffers with minimal delay to avoid size mismatch
unsigned long int swapStart = micros();
#ifdef ARDUINO_FEATHER_ESP32
xSemaphoreTake( _xMutex, portMAX_DELAY ); // ToDo: look into implications of portAX_DELAY
#endif
therm0AMB.swap();
therm0Sto.swap();
#ifdef ARDUINO_FEATHER_ESP32
xSemaphoreGive( _xMutex );
#endif

unsigned long int swapEnd = micros();
//Serial.println("swap: " + String(swapEnd - swapStart));

Expand Down Expand Up @@ -2287,7 +2286,13 @@ bool EmotiBit::processThermopileData()
// if dummy data was stored
if (dataAMB[i] == -2 && dataSto[i] == -2)
{
#ifdef ARDUINO_FEATHER_ESP32
xSemaphoreTake( _xMutex, portMAX_DELAY ); // ToDo: look into implications of portAX_DELAY
#endif
dataDoubleBuffers[(uint8_t)EmotiBit::DataType::THERMOPILE]->swap();
#ifdef ARDUINO_FEATHER_ESP32
xSemaphoreGive( _xMutex );
#endif
return true;
//return dataDoubleBuffers[(uint8_t)EmotiBit::DataType::THERMOPILE]->getData(data, timestamp);
}
Expand Down Expand Up @@ -2351,7 +2356,13 @@ bool EmotiBit::processThermopileData()
therm0Sto.getOverflowCount(DoubleBufferFloat::BufferSelector::OUT)
)
);
#ifdef ARDUINO_FEATHER_ESP32
xSemaphoreTake( _xMutex, portMAX_DELAY ); // ToDo: look into implications of portAX_DELAY
#endif
dataDoubleBuffers[(uint8_t)EmotiBit::DataType::THERMOPILE]->swap();
#ifdef ARDUINO_FEATHER_ESP32
xSemaphoreGive( _xMutex );
#endif
// ToDo: implement logic to determine return val
return true;
}
Expand Down Expand Up @@ -2945,7 +2956,7 @@ void EmotiBit::readSensors()
#ifdef DEBUG_GET_DATA
Serial.println("readSensors()");
#endif // DEBUG
if (DIGITAL_WRITE_DEBUG) digitalWrite(DEBUG_OUT_PIN_0, HIGH);


static unsigned long readSensorsBegin = micros();
if (_debugMode)
Expand Down Expand Up @@ -3225,7 +3236,7 @@ void EmotiBit::readSensors()
}
if (acquireData.debug) pushData(EmotiBit::DataType::DEBUG, micros() - readSensorsBegin); // Add readSensors processing duration to debugBuffer

if (DIGITAL_WRITE_DEBUG) digitalWrite(DEBUG_OUT_PIN_0, LOW);

}

void EmotiBit::processHeartRate()
Expand Down Expand Up @@ -3330,7 +3341,13 @@ void EmotiBit::processData()
}
else
{
#ifdef ARDUINO_FEATHER_ESP32
xSemaphoreTake( _xMutex, portMAX_DELAY ); // ToDo: look into implications of portAX_DELAY
#endif
dataDoubleBuffers[t]->swap();
#ifdef ARDUINO_FEATHER_ESP32
xSemaphoreGive( _xMutex );
#endif
//Serial.print(String(t) + ",");
}
}
Expand Down Expand Up @@ -4404,25 +4421,35 @@ void attachToInterruptTC3(void(*readFunction)(void), EmotiBit* e)
}

#elif defined ARDUINO_FEATHER_ESP32
void onTimer() {
vTaskResume(EmotiBitDataAcquisition);
}
#endif

#ifdef ARDUINO_FEATHER_ESP32
void attachUpdateToCore(void(*readFunction)(void*))
{
// Creating a new task to run emotibit.update()
// Task runs on core 0, along with WiFi tasks.
xTaskCreatePinnedToCore(
*readFunction, /* Task function. */
"EmotiBitDataProcess", /* name of task. */
32000, /* Stack size of task */
NULL, /* parameter of the task */
1, /* priority of the task */
NULL, /* Task handle to keep track of created task */
0); /* pin task to core 0 */
delay(500);
}

void attachToCore(void(*readFunction)(void*), EmotiBit*e)
void attachReadSensorsToCore(void(*readFunction)(void*), EmotiBit*e)
{
attachEmotiBit(e);
// assigning readSensors to second core
// Creating a new task to run data acquisition
// Task runs on core 1, along with main loop.
xTaskCreatePinnedToCore(
*readFunction, /* Task function. */
"EmotiBitDataAcquisition", /* name of task. */
10000, /* Stack size of task */
NULL, /* parameter of the task */
configMAX_PRIORITIES - 1, /* priority of the task */
&EmotiBitDataAcquisition, /* Task handle to keep track of created task */
1); /* pin task to core 0 */
1); /* pin task to core 1 */
delay(500);
}

Expand All @@ -4442,21 +4469,73 @@ void ReadSensors()
}
}
#elif defined ARDUINO_FEATHER_ESP32
void ReadSensors(void *pvParameters)
void Update(void *pvParameter)
{
Serial.print("The data acquisition is executing on core: "); Serial.println(xPortGetCoreID());
while (1) // the function assigned to the second core should never return
Serial.print("Update is executing on core: "); Serial.println(xPortGetCoreID());
while(1)
{
if (myEmotiBit != nullptr)
{
myEmotiBit->readSensors();

myEmotiBit->update();
}
else
{
Serial.println("EmotiBit is nullptr");
}
vTaskSuspend(NULL);
vTaskDelay(pdMS_TO_TICKS(1)); // Delay added to give time to IDLE task
}
}

void ReadSensors(void *pvParameters)
{
Serial.print("The data acquisition is executing on core: "); Serial.println(xPortGetCoreID());
int BASE_TIME_PERIOD = (int)((float) 1000000 / (float) BASE_SAMPLING_FREQ);
uint32_t timeLast = micros();
while (1) // the function should never return, otherwise causes an exception in RTOS
{
if(micros() - timeLast > BASE_TIME_PERIOD)
{
timeLast = micros();
if (myEmotiBit != nullptr)
{
xSemaphoreTake( _xMutex, portMAX_DELAY ); // ToDo: look into implications of portAX_DELAY
if (myEmotiBit->DIGITAL_WRITE_DEBUG) digitalWrite(myEmotiBit->DEBUG_OUT_PIN_0, HIGH);
myEmotiBit->readSensors();
if (myEmotiBit->DIGITAL_WRITE_DEBUG) digitalWrite(myEmotiBit->DEBUG_OUT_PIN_0, LOW);
xSemaphoreGive( _xMutex );
}
else
{
Serial.println("EmotiBit is nullptr");
}

if(myEmotiBit->_freeToSleep && myEmotiBit->_emotiBitWiFi._wifiOff)
{
int sleepTime;
// ToDo: get more accurate buffer time
uint32_t wakeupTimebuffer = 600; // in uS. Empirically derived estimate. Time taken by CPU to wake up
// ToDo: move this into a calculateSleepTime() function
if((timeLast + BASE_TIME_PERIOD) - micros() > wakeupTimebuffer)
{
// calculate time to sleep
sleepTime = (int)(timeLast + BASE_TIME_PERIOD) - (int)micros() - (int)wakeupTimebuffer;
}

if(sleepTime > wakeupTimebuffer)
{
// only sleep if we can wake up in time
esp_sleep_enable_timer_wakeup(sleepTime); // every 6.6mS to maintain 150Hz base sampling
esp_light_sleep_start(); // start light sleep
}
}
else
{
// ToDo: Schedule time give to idle task.
vTaskDelay(pdMS_TO_TICKS(1)); // if not sleeping, give time-slice to IDLE task
}

}

}

}
Expand Down
14 changes: 9 additions & 5 deletions EmotiBit.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,14 +50,14 @@ class EmotiBit {



String firmware_version = "1.9.0";
String firmware_version = "1.9.0.feat-2Core.4";



TestingMode testingMode = TestingMode::NONE;
const bool DIGITAL_WRITE_DEBUG = false;
const bool DIGITAL_WRITE_DEBUG = true;
#if defined (ARDUINO_FEATHER_ESP32)
const uint8_t DEBUG_OUT_PIN_0 = 26;
const uint8_t DEBUG_OUT_PIN_0 = 21;
const uint8_t DEBUG_OUT_PIN_1 = 33;
const uint8_t DEBUG_OUT_PIN_2 = 15;
#elif defined (ADAFRUIT_FEATHER_M0)
Expand Down Expand Up @@ -425,6 +425,8 @@ class EmotiBit {
DataType _serialData = DataType::length;
volatile bool buttonPressed = false;
bool startBufferOverflowTest = false;
bool _freeToSleep = false;


void setupFailed(const String failureMode, int buttonPin = -1);
bool setupSdCard();
Expand Down Expand Up @@ -673,6 +675,7 @@ class EmotiBit {

const uint8_t SCOPE_TEST_PIN = A0;
bool scopeTestPinOn = false;


};

Expand All @@ -681,9 +684,10 @@ void attachEmotiBit(EmotiBit*e = nullptr);
void attachToInterruptTC3(void(*readFunction)(void), EmotiBit*e = nullptr);
void ReadSensors();
#elif defined ARDUINO_FEATHER_ESP32
void onTimer();
void attachToCore(void(*readFunction)(void*), EmotiBit*e = nullptr);
void attachReadSensorsToCore(void(*readFunction)(void*), EmotiBit*e = nullptr);
void ReadSensors(void* pvParameters);
void Update(void* pvParameters);
void attachUpdateToCore(void(*readFunction)(void*));
#endif


Expand Down
11 changes: 8 additions & 3 deletions EmotiBit_stock_firmware/EmotiBit_stock_firmware.ino
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,13 @@ void setup()

void loop()
{
//Serial.println("emotibit.update()");
emotibit.update();

// Main loop is attached to a different task spawned by default. In our current scheme,
// main loop runs run when acquisitoin task pasuses by calling vTaskDelay()

// The update function has been moved to its separate task.
//emotibit.update();

// ToDo: assess time slice given to this task once ported to 2-core 2-task architecture
size_t dataAvailable = emotibit.readData(EmotiBit::DataType::PPG_GREEN, &data[0], dataSize);
if (dataAvailable > 0)
{
Expand All @@ -70,4 +74,5 @@ void loop()
}
}
}

}
Loading