diff --git a/.gitignore b/.gitignore index 7f94078..679ac69 100644 --- a/.gitignore +++ b/.gitignore @@ -39,4 +39,6 @@ src/main.cpp # VS Code -**/.vscode \ No newline at end of file +**/.vscode +**/.vscode/c_cpp_properties.json +**/.vscode/settings.json \ No newline at end of file diff --git a/README.md b/README.md index c060436..d2cabc4 100644 --- a/README.md +++ b/README.md @@ -17,13 +17,16 @@ This firmware is compatible with hardware version: ## Table of Contents - [Introduction](#Introduction) + - [Note about v1.4.0](#note-about-v140) - [Setup](#Setup) - - [Arduino IDE](#Arduino-IDE) - - [Arduino Libraries](#Arduino-Libraries) - - [SD Card Setup](#SD-Card-Setup) - - [SPI Setup](#SPI-Setup) - - [sdfat Library Setup](#sdfat-Library-Setup) - - [BMP280 Library Setup](#BMP280-Library-Setup) + - [Automatic setup (Preferred)](#automatic-setup-preferred-method) + - [Manual setup (Not recommended)](#manual-setup-not-recommended) + - [Arduino IDE](#Arduino-IDE) + - [Arduino Libraries](#Arduino-Libraries) + - [SD Card Setup](#SD-Card-Setup) + - [SPI Setup](#SPI-Setup) + - [sdfat Library Setup](#sdfat-Library-Setup) + - [BMP280 Library Setup](#BMP280-Library-Setup) - [Usage](#Usage) - [Install OpenEarable](#Install-OpenEarable) - [Default Firmware](#Default-Firmware) @@ -44,16 +47,79 @@ This firmware is compatible with hardware version: OpenEarable is designed to enable ear-based sensing applications by offering a flexible and open-source hardware platform. It incorporates a range of sensors, including a 9-axis inertial measurement unit, an ear canal pressure and temperature sensor, an inward-facing ultrasound microphone, a speaker, a push button, and a controllable RGB LED. With these features, OpenEarable provides researchers and developers with the ability to explore various application scenarios. For more information visit the [OpenEarable](https://open-earable.teco.edu/) website. -OpenEarable is controlled and streams sensor data via BLE (Bluetooth Low Energy). Audio is played from and recorded to the internal SD card (required card SanDisk Extreme Class 3, must be formatted as exFAT). OpenEarable is compatible with the provided [dashboard](https://github.com/OpenEarable/dashboard) and [edge-ml](https://edge-ml.org/). +OpenEarable is controlled and streams sensor data via BLE (Bluetooth Low Energy). Audio is played from and recorded to the internal SD card (required card SanDisk Extreme Class 3, must be formatted as exFAT). OpenEarable is compatible with the provided [dashboard](https://github.com/OpenEarable/dashboard) and [edge-ml](https://edge-ml.org/). + +### Note about firmwares >v1.4.0 + +#### SD card logging + +Version 1.4.0 of the firmware is designed for offline data collection. By default, all data (IMU, Baro/Temp and Audio) are logged on the SD card. **Choose a very fast SDCard!!** Here's some examples we tested: +- Samsung EVO Plus +- SanDisk Extreme Pro + +Note that even for the same class, not all SDcard from different manufacturers have the same performance!! + +#### Bluetooth streaming + +Because the main focus is to log the data locally on the SD card, this version of the firmware streams limited amount of data over BLE. By default, the streaming over BLE happens at 10 Hz, no matter what sampling rate you set for the recordings. + +#### RGB led + +In order to let researcher know whether the device is recording or not without being always connected to the dashboard, the RGB led is used to show the recording status. If a certain colour is on, then the corresponding sensor data is being recorded: + +- Red: Microphone +- Green: Baro/Temp +- Blue: IMU + +Obviously, any combination of sensors enabled/disable will result in a mixed colour for the led (within the hardware limits!!) (i.e. if microphone and baro/tempo are being recorded then the led would be yellow). + +#### Device naming + +The new firmware introduces the possibility to change the names of the devices programmatically. This simplifies the usage in data collections where a left and a right earable are used. By default, the name is "OpenEarable" as per previous firmwares. +The left or right names can be given when programming and **MUST BE** `OELeft` or `OERight`. +The firmware will then + +#### Dashboard + +In order to use the new version of the firmware, especially if the custom names are used for left/right earables, we suggest to use the modified dashboard available [here](https://github.com/ThiasTux/openearable-dashboard). ## Setup **⚠️ $${\rm\color{red}Please~Note:}$$ We recommend deploying the firmware using the [open-earable-PlatformIO wrapper](https://github.com/OpenEarable/open-earable-PlatformIO).** -### Arduino IDE +### Automatic setup (Preferred method!!) + +Clone this repo in your Arduino library folder, usually in under `Documents/Arduino/libraries`: + +```bash +git clone https://github.com/ThiasTux/open-earable OpenEarable +``` + +If you are on Linux or macOS, you can setup you environment using the script in `script/install.sh`. In order to so, please install the `arduino-cli` following the guide [here](https://arduino.github.io/arduino-cli/0.35/installation/). + +#### macOS +For macos using brew: + +```bash +brew install arduino-cli +``` + +#### Env setup + +To setup the environment, navigate to `scripts` and then run: + +```bash +./install.sh +``` + +The script uses `arduino-cli` to download and install all the dependencies (i.e. board, libraries, etc.) and to make the required changes to the libraries. The script also generate a new board so that the original Mbed OS Nano installation remains untouched in case you are developing also with other Nano boards (plus it doesn't get touched if the Nano board installation gets updated). + +### Manual setup (Not recommended) + +#### Arduino IDE Download and install the Arduino IDE. OpenEarable is based on the "Arduino Nano 33 BLE Sense" board. Therefore, you first have to install the required dependencies ("Arduino Mbed OS Nano Boards" via boards manager) in your Arduino IDE following this [Setup Guide](https://docs.arduino.cc/software/ide-v2/tutorials/ide-v2-board-manager#mbed-os-nano). -### Arduino Libraries +#### Arduino Libraries The following Arduino Libraries have to be installed in your Arduino IDE by navigating to `Sketch -> Include Library -> Manage Libraries`: - [EdgeML-Arduino (version 1.3.3)](https://github.com/edge-ml/EdgeML-Arduino), which includes the following dependencies that are also required and automatically installed: - [ArduinoBLE](https://github.com/arduino-libraries/ArduinoBLE) @@ -62,11 +128,11 @@ The following Arduino Libraries have to be installed in your Arduino IDE by navi - [SdFat - Adafruit Fork](https://github.com/adafruit/SdFat) -### SD Card Setup +#### SD Card Setup In order to be compatible with the OpenEarable library the SD card needs to be formatted with the exFAT format. Make sure to have a sufficiently fast SD card. (Required SD Card: SandDisk class 10 and class A30) -### SPI, Wire, and Variant Setup +#### SPI, Wire, and Variant Setup The default Arduino implementation of the SPI library does not meet the required speed. To address this, optimized SPI files are provided. Follow the steps below to integrate these files into Arduino. All referenced files can be found in the "resources" folder in the "spi_files" subfolder. @@ -93,7 +159,7 @@ To fully integrate the optimized SPI files, changes to the Arduino Nano 33 BLE b 10. Navigate back to the `Arduino15` folder. Navigate to `packages/arduino/hardware/mbed_nano/4.0.4/variants/ARDUINO_NANO33BLE`. Replace `pins_arduino.h` and `variant.cpp `with the files provided under `resources/variant` of this repository. -### sdFat Library Setup +#### sdFat Library Setup One of the library dependencies is the SdFat library from Bill Greiman. This library is used to send data to the SD card. To achieve the desired write speeds of up to 1.5Mbps the library has to be modified slighlty. @@ -102,16 +168,17 @@ To achieve the desired write speeds of up to 1.5Mbps the library has to be modif 3. Inside the `src` folder, replace the `SdFatConfig.h` with the provided `SdFatConfig.h` file found in the `resources/sdfat_config` folder of this repository. -### BMP280 Library Setup +#### BMP280 Library Setup The BMP280 library has to be slightly modified. 1. Go to the `Arduino/libraries` folder (commonly found in your `Documents` folder) and locate the `Adafruit_BMP280_Library` folder. 2. Replace the files `Adafruit_BMP280.cpp` and `Adafruit_BMP280.h` with the files found in the `resources/Adafruit_BMP280_Library` folder of this repository. -### Reboot +#### Reboot To make sure that all your changes are applied correctly, restart your computer. ## Usage + ### Install OpenEarable Now that all dependencies are configured, the last step is to install this repository as a library as follows: @@ -146,7 +213,7 @@ void loop() With this minimum sketch, all internal functionality is activated and OpenEarable becomes controllable via our [Dashboard](https://github.com/OpenEarable/dashboard), via [EdgeML](https://edge-ml.org/), and via the BLE API. ### Flashing -To flash the firmware, make sure you select `Arduino Nano 33 BLE` as target and the port that your OpenEarable is connected to. Then simply press the `Upload` arrow. +To flash the firmware, make sure you select `Openearable` as target (or `Arduino Nano 33 BLE` if you used the manual setup procedure) and the port that your OpenEarable is connected to. Then simply press the `Upload` arrow. ### Dashboard diff --git a/examples/App/App.ino b/examples/App/App.ino index 2f6bf2d..744696c 100644 --- a/examples/App/App.ino +++ b/examples/App/App.ino @@ -4,24 +4,29 @@ * OpenEarable Dashboard: openearable.github.io/dashboard/ * edge-ml: app.edge-ml.org * - * Firmware-version: 1.3.0 - * Release-date: 6.10.2023 + * Firmware-version: 1.4.1 + * Release-date: 17.06.2024 */ #include "Arduino.h" #include "OpenEarable.h" // Set DEBUG to true in order to enable debug print -#define DEBUG false +#define DEBUG true + +// Change name to OELeft or OERight before flashing ("OpenEarable" if left as default value) +String d_name = "OpenEarable"; void setup() { #if DEBUG Serial.begin(115200); + delay(5000); open_earable.debug(Serial); + delay(5000); #endif - open_earable.begin(); + open_earable.begin(d_name); } void loop() diff --git a/library.properties b/library.properties index 80fa17d..706ed8b 100644 --- a/library.properties +++ b/library.properties @@ -1,11 +1,11 @@ name=OpenEarable -version=1.3.0 -author=TECO / KIT -maintainer=TECO +version=1.4.1 +author=Mathias / Mobisys +maintainer=Mathias sentence=Firmware to use OpenEarable with the provided dashboard and edge-ml.org. paragraph=The firmware exposes a BLE API to control the IMU, pressure sensor, microphone, RGB LED and audio player. It also notifies about button state and battery changes. category=Sensors architectures=mbed_nano, nrf52 -url=https://github.com/OpenEarable/open-earable/tree/v1.3.0 +url=https://github.com/ThiasTux/open-earable includes=OpenEarable.h depends=EdgeML-Arduino, ArduinoBLE, Arduino_LPS22HB, STM32duino LSM6DSR, Adafruit BMP280 Library, DFRobot_BMX160 diff --git a/scripts/install.sh b/scripts/install.sh new file mode 100755 index 0000000..1d2c8e8 --- /dev/null +++ b/scripts/install.sh @@ -0,0 +1,125 @@ +#!/bin/bash +# Install board +echo "Installing mbed_nano board version" +arduino-cli core install arduino:mbed_nano + +# Install dependecies +# Install EDGEML (1.3.3) +echo "Installing libraries..." +arduino-cli lib install EdgeML-Arduino@1.3.3 +arduino-cli lib install 'Adafruit BMP280 Library' +arduino-cli lib install "DFRobot_BMX160" +arduino-cli lib install "SdFat - Adafruit Fork" + +tmp_output=$(arduino-cli core search arduino:mbed_nano | grep arduino:mbed_nano) +MBED_NANO_VERSION=$(echo "$tmp_output" | grep -Eo '[0-9]+\.[0-9]+\.[0-9]+') +$version_number + +echo $MBED_NANO_VERSION + +OS=$(uname) + +# Path to your Arduino15 folder (replace with your actual path) +if [[ "$OS" == "Linux" ]]; then + echo "This is a Linux system." + ARDUINO_15_PATH="/home/$(whoami)/.arduino15" +elif [[ "$OS" == "Darwin" ]]; then + echo This is a macOS system. + ARDUINO_15_PATH="/Users/$(whoami)/Library/Arduino15" +fi + +echo $ARDUINO_15_PATH + +# Resources folder path (replace with your actual path) +RESOURCES_PATH="../resources" + +# Check if Arduino15 folder exists +if [ ! -d "$ARDUINO_15_PATH" ]; then + echo "Error: Arduino15 folder not found at $ARDUINO_15_PATH" + exit 1 +fi + +# Check if Resources folder exists +if [ ! -d "$RESOURCES_PATH" ]; then + echo "Error: Resources folder not found at $RESOURCES_PATH" + exit 1 +fi + +# SPI, Wire, and Variant Setup +SPI_DIR="$RESOURCES_PATH/spi_files/SPI" +WIRE_DIR="$RESOURCES_PATH/wire_files/Wire" +VARIANT_DIR="$RESOURCES_PATH/variant" + +# Create new board for OpenEarable +BOARD_DIR="$ARDUINO_15_PATH/packages/arduino/hardware/mbed_nano/$MBED_NANO_VERSION" +NEW_BOARD_DIR="$ARDUINO_15_PATH/packages/arduino/hardware/openearable/$MBED_NANO_VERSION" + +echo "Creating folder for new Openearable board..." +mkdir "$ARDUINO_15_PATH/packages/arduino/hardware/openearable" +cp -R $BOARD_DIR $NEW_BOARD_DIR +rm -rf "$NEW_BOARD_DIR/variants/NANO_RP20240_CONNECT" + +echo "Replacing board names..." +if [[ "$OS" == "Linux" ]]; then + sed -i '/^nanorp2040connect/d' "$NEW_BOARD_DIR/boards.txt" + sed -i 's/nano33ble.name=Arduino Nano 33 BLE/nano33ble.name=Openearable/' "$NEW_BOARD_DIR/boards.txt" +elif [[ "$OS" == "Darwin" ]]; then + sed -i="" '/^nanorp2040connect/d' "$NEW_BOARD_DIR/boards.txt" + sed -i="" 's/nano33ble.name=Arduino Nano 33 BLE/nano33ble.name=Openearable/' "$NEW_BOARD_DIR/boards.txt" +fi + +LIB_DIR="$NEW_BOARD_DIR/libraries/" + +# Replace SPI library +echo "Replacing SPI library..." +rm -rf "$LIB_DIR/SPI" +cp -r "$SPI_DIR" "$LIB_DIR" + +# Replace Wire library +echo "Replacing Wire library..." +rm -rf "$LIB_DIR/Wire" +cp -r "$WIRE_DIR" "$LIB_DIR" + +# Copy RingBuffer files +echo "Copying RingBuffer files..." +cp "$RESOURCES_PATH/wire_files/RingBuffer.h" "$ARDUINO_15_PATH/packages/arduino/hardware/openearable/$MBED_NANO_VERSION/cores/arduino" +cp "$RESOURCES_PATH/wire_files/RingBuffer.cpp" "$ARDUINO_15_PATH/packages/arduino/hardware/openearable/$MBED_NANO_VERSION/cores/arduino" + +# Replace nrfx_spim files +echo "Replacing nrfx_spim files..." +cp "$RESOURCES_PATH/spi_files/nrfx_spim.h" "$ARDUINO_15_PATH/packages/arduino/hardware/openearable/$MBED_NANO_VERSION/cores/arduino/mbed/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_SDK_15_0/modules/nrfx/drivers/include/" +cp "$RESOURCES_PATH/spi_files/nrfx_spim.c" "$ARDUINO_15_PATH/packages/arduino/hardware/openearable/$MBED_NANO_VERSION/cores/arduino/mbed/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_SDK_15_0/modules/nrfx/drivers/src/" + +# Replace variant files +echo "Replacing variant files..." +cp "$RESOURCES_PATH/variant/pins_arduino.h" "$ARDUINO_15_PATH/packages/arduino/hardware/openearable/$MBED_NANO_VERSION/variants/ARDUINO_NANO33BLE" +cp "$RESOURCES_PATH/variant/variant.cpp" "$ARDUINO_15_PATH/packages/arduino/hardware/openearable/$MBED_NANO_VERSION/variants/ARDUINO_NANO33BLE" + +# sdFat Library Setup +SDFAT_LIB_DIR="$HOME/Documents/Arduino/libraries/SdFat_-_Adafruit_Fork/src" # Update path if needed + +# Check if sDFat library exists +if [ ! -d "$SDFAT_LIB_DIR" ]; then + echo "Warning: SdFat library not found at $SDFAT_LIB_DIR" +echo "Please install the SdFat library from Bill Greiman." +exit 1 +fi +# Replace SdFatConfig.h +echo "Replacing SdFatConfig.h..." +cp "$RESOURCES_PATH/sdfat_config/SdFatConfig.h" "$SDFAT_LIB_DIR/" + +# sdFat Library Setup +BMP280_LIB_DIR="$HOME/Documents/Arduino/libraries/Adafruit_BMP280_Library" # Update path if needed + +# Check if sDFat library exists +if [ ! -d "$BMP280_LIB_DIR" ]; then + echo "Warning: BMP280 library not found at $BMP280_LIB_DIR" +echo "Please install the BMP280 library." +exit 1 +fi +# Replace SdFatConfig.h +echo "Replacing BMP280 Files..." +cp "$RESOURCES_PATH/Adafruit_BMP280_Library/Adafruit_BMP280.cpp" "$BMP280_LIB_DIR/" +cp "$RESOURCES_PATH/Adafruit_BMP280_Library/Adafruit_BMP280.h" "$BMP280_LIB_DIR/" + +echo "Done. If you have issues, please reboot first." \ No newline at end of file diff --git a/src/.vscode/settings.json b/src/.vscode/settings.json deleted file mode 100644 index 5633f79..0000000 --- a/src/.vscode/settings.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "files.associations": { - "*.cps": "javascript", - "type_traits": "cpp", - "limits": "cpp", - "chrono": "cpp", - "algorithm": "cpp", - "typeinfo": "cpp", - "__locale": "cpp", - "string": "cpp", - "string_view": "cpp", - "__string": "cpp", - "istream": "cpp", - "*.txt": "cpp", - "cstddef": "cpp" - } -} \ No newline at end of file diff --git a/src/OpenEarable.h b/src/OpenEarable.h index 30d39c9..e1c2c2f 100644 --- a/src/OpenEarable.h +++ b/src/OpenEarable.h @@ -6,7 +6,6 @@ #define EDGE_ML_EARABLE_EDGEML_EARABLE_H #include -//#include #include "EdgeML.h" #include @@ -28,15 +27,18 @@ #include -#include +#include +#include #include -const String device_name = "OpenEarable"; +String device_name; const String firmware_version = "1.4.0"; const String hardware_version = "1.4.0"; -bool _data_logger_flag = false; +bool _data_logger_flag = true; + +uint8_t led_color[3] = {0, 0, 0}; void data_callback(int id, unsigned int timestamp, uint8_t * data, int size); void config_callback(SensorConfigurationPacket *config); @@ -45,8 +47,9 @@ class OpenEarable { public: OpenEarable() = default; - void begin() { - Serial.begin(0); + void begin(String d_name) { + + device_name = d_name; _interface = new SensorManager_Earable(); _battery = new Battery_Service(); @@ -54,14 +57,18 @@ class OpenEarable { sd_manager.begin(); edge_ml_generic.set_config_callback(config_callback); - //edge_ml_generic.set_data_callback(data_callback); + edge_ml_generic.set_data_callback(data_callback); if (_debug) { _battery->debug(*_debug); + IMULogger::debug(*_debug); + BAROLogger::debug(*_debug); + Recorder::debug(*_debug); } if (_data_logger_flag) { - SD_Logger::begin(); + IMULogger::begin(); + BAROLogger::begin(); } // Can both be initialized without extra cost @@ -130,19 +137,53 @@ class OpenEarable { static void data_callback(int id, unsigned int timestamp, uint8_t * data, int size) { if (_data_logger_flag) { String data_string = edge_ml_generic.parse_to_string(id, data); - SD_Logger::data_callback(id, timestamp, data_string); + if (id == BARO_TEMP) BAROLogger::data_callback(id, timestamp, data_string); + else if (id == ACC_GYRO_MAG) IMULogger::data_callback(id, timestamp, data_string); } } static void config_callback(SensorConfigurationPacket *config); + static void update_current_led_status(SensorConfigurationPacket *config); }; OpenEarable open_earable; +void OpenEarable::update_current_led_status(SensorConfigurationPacket *config){ + + if (config->sensorId == PDM_MIC) { + if (int(config->sampleRate) > 0) { + led_color[0] = 255; + } else { + led_color[0] = 0; + } + } else if (config->sensorId == BARO_TEMP) { + if (int(config->sampleRate) > 0) { + led_color[1] = 255; + } else { + led_color[1] = 0; + } + } else if (config->sensorId == ACC_GYRO_MAG) { + if (int(config->sampleRate) > 0) { + led_color[2] = 255; + } else { + led_color[2] = 0; + } + } + + if (open_earable._debug) { + open_earable._debug->println("Current_status: "); + for (int i = 0; i < 3; i++) { + open_earable._debug->println(led_color[i]); + } + } + + earable_led.set_color(led_color); +} + void OpenEarable::config_callback(SensorConfigurationPacket *config) { if (config->sensorId == PDM_MIC) Recorder::config_callback(config); - else if (config->sensorId == BARO_TEMP) task_manager.begin(config->sampleRate, -1); - else if (config->sensorId == ACC_GYRO_MAG) task_manager.begin(-1, config->sampleRate); + else if (config->sensorId == BARO_TEMP) BAROLogger::config_callback(config); + else if (config->sensorId == ACC_GYRO_MAG) IMULogger::config_callback(config); else { if (i2s_player.is_running()) { i2s_player.stop(); @@ -150,6 +191,7 @@ void OpenEarable::config_callback(SensorConfigurationPacket *config) { i2s_player.start(); } } + update_current_led_status(config); //else task_manager.begin(); } diff --git a/src/audio_pdm/PDM_Mic.cpp b/src/audio_pdm/PDM_Mic.cpp index 8e8fdce..b24f119 100644 --- a/src/audio_pdm/PDM_Mic.cpp +++ b/src/audio_pdm/PDM_Mic.cpp @@ -104,7 +104,7 @@ bool PDM_Mic::begin() { case 1: if (_gain_l < 0) { - // right mic active (right and left gain need to be swapped) + // right mic nrf_pdm_mode_set(NRF_PDM_MODE_MONO, NRF_PDM_EDGE_LEFTRISING); nrf_pdm_gain_set(constrain(_gain_r, 0x00, 0x50), constrain(_gain_l, 0x00, 0x50)); } else { @@ -112,7 +112,7 @@ bool PDM_Mic::begin() { nrf_pdm_mode_set(NRF_PDM_MODE_MONO, NRF_PDM_EDGE_LEFTFALLING); nrf_pdm_gain_set(constrain(_gain_l, 0x00, 0x50), constrain(_gain_r, 0x00, 0x50)); } - + break; default: @@ -212,7 +212,7 @@ int PDM_Mic::setSampleRate(int sampleRate) { void PDM_Mic::setGain(int8_t gain_left, int8_t gain_right) { _gain_l = gain_left; _gain_r = gain_right; - + if (_available) { if (_channels == 1 && _gain_l < 0) { // right mic active (right and left gain need to be swapped) diff --git a/src/audio_pdm/Recorder.cpp b/src/audio_pdm/Recorder.cpp index 5d3b454..a7d650e 100644 --- a/src/audio_pdm/Recorder.cpp +++ b/src/audio_pdm/Recorder.cpp @@ -5,6 +5,8 @@ uint8_t PDM_BUFFER[pdm_b_size * pdm_b_count] __attribute__((aligned (16))); +Stream * _rec_debug{}; + Recorder::Recorder() { } @@ -51,6 +53,11 @@ void Recorder::end() { _available = false; } +void Recorder::debug(Stream &stream) { + _rec_debug = &stream; + _rec_debug->println("Recorder debug set correctly!"); +} + void Recorder::print_info() { Serial.println("RECORDER INFO:"); Serial.print("channels: "); @@ -137,8 +144,8 @@ void Recorder::config_callback(SensorConfigurationPacket *config) { // Check for valid sample rate recorder.setSampleRate(sample_rate); - int8_t gain_l = config->latency & 0xFF; - int8_t gain_r = (config->latency >> 8) & 0xFF; + int8_t gain_r = config->latency & 0xFF; + int8_t gain_l = (config->latency >> 8) & 0xFF; // number of channels recorder.setChannels((gain_l >= 0) + (gain_r >= 0)); @@ -177,10 +184,15 @@ void Recorder::config_callback(SensorConfigurationPacket *config) { // file name of the new recording String file_name = "/" + recording_dir + "/Recording_" + String(n) + "_" + String(millis()) + ".wav"; - // set WaveRecorder - recorder.setTarget(new WavRecorder(file_name)); - } + if (_rec_debug) { + _rec_debug->println("Recording:"); + _rec_debug->println(file_name); + } + + // set WaveRecorder + recorder.setTarget(new WavRecorder(file_name)); + } // Start pdm mic recorder.record(); } diff --git a/src/audio_pdm/Recorder.h b/src/audio_pdm/Recorder.h index edd7686..1202dbb 100644 --- a/src/audio_pdm/Recorder.h +++ b/src/audio_pdm/Recorder.h @@ -5,12 +5,13 @@ #include "EdgeML_Custom.h" #include "utils/SDManager.h" +#include #include "AudioTarget.h" #include "utils/BufferedInputStream.h" #include "InputDevice.h" -const int pdm_b_size = 6144; //4096; +const int pdm_b_size = 4096; //4096; const int pdm_b_count = 8; extern uint8_t PDM_BUFFER[pdm_b_size * pdm_b_count] __attribute__((aligned (16))); @@ -37,6 +38,7 @@ class Recorder { void print_info(); static void config_callback(SensorConfigurationPacket * config); + static void debug(Stream &stream); AudioTarget * target; InputDevice * device; diff --git a/src/audio_play/SOSFilter.cpp b/src/audio_play/SOSFilter.cpp index 705feaf..cebcd2c 100644 --- a/src/audio_play/SOSFilter.cpp +++ b/src/audio_play/SOSFilter.cpp @@ -10,7 +10,7 @@ SOSFilter::SOSFilter(int order, const float (*b)[3], const float (*a)[3]) { //buffer.resize(order, std::vector(2, 0.0f)); buffer = new float*[order]; for (int i = 0; i < order; ++i) { - buffer[i] = new float[2]{0.0f, 0.0f}; + buffer[i] = new float[2]{0.0f, 0.0f}; // Speicher für 2 Verzögerungen pro Sektion } } diff --git a/src/battery_service/Battery_Service.cpp b/src/battery_service/Battery_Service.cpp index 2b75d2c..cb50db9 100644 --- a/src/battery_service/Battery_Service.cpp +++ b/src/battery_service/Battery_Service.cpp @@ -11,11 +11,11 @@ void Battery_Service::update() { if (battery->check_battery()) { const int battery_level = battery->get_battery_level(); const int charing_state = battery->get_charging_state(); - if (_debug) { - _debug->print("Battery Level % is now: "); - _debug->println(battery_level); - _debug->println(charing_state); - } + // if (_debug) { + // _debug->print("Battery Level % is now: "); + // _debug->println(battery_level); + // _debug->println(charing_state); + // } batteryLevelC->setValue(battery_level); chargingStateC->setValue(charing_state); } diff --git a/src/sd_logger/BARO_Logger.cpp b/src/sd_logger/BARO_Logger.cpp new file mode 100644 index 0000000..a7204b9 --- /dev/null +++ b/src/sd_logger/BARO_Logger.cpp @@ -0,0 +1,132 @@ +#include "BARO_Logger.h" + +#include + +ExFatFile BAROLogger::_file; +bool BAROLogger::_opened = false; +char BAROLogger::_buffer[LOGGER_BUFFER_SIZE]; +int BAROLogger::_index = 0; +String BAROLogger::_name = "Baro.csv"; +Stream * _baro_debug{}; + +bool BAROLogger::begin() { + _index = 0; + if(!sd_manager.begin()) return false; + return true; +} + +void BAROLogger::debug(Stream &stream) { + _baro_debug = &stream; + _baro_debug->println("BAROLogger debug set correctly!"); +} + +void BAROLogger::end() { + //sd_manager.end(); +} + +void BAROLogger::set_name(String name) { + _name = std::move(name); + _opened = false; +} + +void BAROLogger::data_callback(int id, unsigned int timestamp, const String & data_string) { + if (id == -1) { + dump_to_sd(); + _file.close(); + _opened = false; + return; + }; + + String text = String(timestamp); + text += ", " + data_string; + text += "\r\n"; + + if (text.length() + _index > LOGGER_BUFFER_SIZE) { + dump_to_sd(); + } + + text.toCharArray(&(_buffer[_index]), text.length()); + _index += text.length() - 1; // -1 to remove null terminator +} + +void BAROLogger::config_callback(SensorConfigurationPacket *config) { + + if (config->sampleRate == 0) { + if (_opened){ + dump_to_sd(); + _file.close(); + _opened = false; + } + return; + } + + if (_baro_debug) _baro_debug->println("Initialising baro file"); + if (!open_file()){ + if (_baro_debug) _baro_debug->println("Error opening the BARO file"); + return; + } + write_header(); + if (_file.isOpen()) + task_manager.begin(config->sampleRate, -1); +} + +void BAROLogger::dump_to_sd() { + if (!open_file()) return; + if (_index == 0) return; + sd_manager.write_block(&_file, (uint8_t*)_buffer, _index); + memset(_buffer, 0, LOGGER_BUFFER_SIZE); + _index = 0; +} + +void BAROLogger::write_header() { + _index = 0; + String header = "timestamp,temp,pressure\n\r "; + header.toCharArray(&(_buffer[_index]), header.length()); + _index += header.length() - 1; // -1 to remove null terminator + dump_to_sd(); +} + +bool BAROLogger::open_file() { + if (_opened) return true; + // find the next available file name for the recording + const String logs_dir = "Baro"; + + if (!sd_manager.exists(logs_dir)) sd_manager.mkdir(logs_dir); + + ExFile file; + ExFile dir = sd_manager.sd->open(logs_dir); + + char fileName[64]; + char * split; + + int n = 1; + + // find highest Recording number + while (file = dir.openNextFile()) { + file.getName(fileName, sizeof(fileName)); + + split = strtok(fileName, "_"); + if (strcmp(split,"Baro") == 0) { + split = strtok(NULL, "_"); + n = max(n, atoi(split) + 1); + } + } + + // file name of the new recording + _name = "/" + logs_dir + "/Baro_" + String(n) + "_" + String(millis()) + ".csv"; + + if (_baro_debug) { + _baro_debug->println("Log filename:"); + _baro_debug->println(_name); + } + + _file = sd_manager.openFile(_name, true); + _opened = _file.isOpen(); + return _opened; +} + + + + + + diff --git a/src/sd_logger/BARO_Logger.h b/src/sd_logger/BARO_Logger.h new file mode 100644 index 0000000..89b5120 --- /dev/null +++ b/src/sd_logger/BARO_Logger.h @@ -0,0 +1,36 @@ +#ifndef OPEN_EARABLE_BARO_LOGGER_H +#define OPEN_EARABLE_BARO_LOGGER_H + +#include "utils/SDManager.h" +#include "EdgeML_Custom.h" +#include +#include + +// #define LOGGER_BUFFER_SIZE 1024 +#define LOGGER_BUFFER_SIZE 2048 + +class BAROLogger{ +public: + static bool begin(); + static void end(); + + static void set_name(String name); + static void debug(Stream &stream); + + static void data_callback(int, unsigned int, const String&); + static void config_callback(SensorConfigurationPacket * config); +private: + static ExFatFile _file; + static bool _opened; + + static int _index; + static char _buffer[LOGGER_BUFFER_SIZE]; + + static String _name; + + static void dump_to_sd(); + static void write_header(); + static bool open_file(); +}; + +#endif //OPEN_EARABLE_SD_LOGGER_H diff --git a/src/sd_logger/IMU_Logger.cpp b/src/sd_logger/IMU_Logger.cpp new file mode 100644 index 0000000..27350b3 --- /dev/null +++ b/src/sd_logger/IMU_Logger.cpp @@ -0,0 +1,132 @@ +#include "IMU_Logger.h" + +#include + +ExFatFile IMULogger::_file; +bool IMULogger::_opened = false; +char IMULogger::_buffer[LOGGER_BUFFER_SIZE]; +int IMULogger::_index = 0; +String IMULogger::_name = "Imu.csv"; +Stream * _imu_debug{}; + +bool IMULogger::begin() { + _index = 0; + if(!sd_manager.begin()) return false; + return true; +} + +void IMULogger::debug(Stream &stream) { + _imu_debug = &stream; + _imu_debug->println("IMULogger debug set correctly!"); +} + +void IMULogger::end() { + //sd_manager.end(); +} + +void IMULogger::set_name(String name) { + _name = std::move(name); + _opened = false; +} + +void IMULogger::data_callback(int id, unsigned int timestamp, const String & data_string) { + if (id == -1) { + dump_to_sd(); + _file.close(); + _opened = false; + return; + }; + + String text = String(timestamp); + text += ", " + data_string; + text += "\r\n"; + + if (text.length() + _index > LOGGER_BUFFER_SIZE) { + dump_to_sd(); + } + + text.toCharArray(&(_buffer[_index]), text.length()); + _index += text.length() - 1; // -1 to remove null terminator +} + +void IMULogger::config_callback(SensorConfigurationPacket *config) { + + if (config->sampleRate == 0) { + if (_opened){ + dump_to_sd(); + _file.close(); + _opened = false; + } + return; + } + + if (_imu_debug) _imu_debug->println("Initialising imu file"); + if (!open_file()){ + if (_imu_debug) _imu_debug->println("Error opening the IMU file"); + return; + } + write_header(); + if (_file.isOpen()) + task_manager.begin(-1, config->sampleRate); +} + +void IMULogger::dump_to_sd() { + if (!open_file()) return; + if (_index == 0) return; + sd_manager.write_block(&_file, (uint8_t*)_buffer, _index); + memset(_buffer, 0, LOGGER_BUFFER_SIZE); + _index = 0; +} + +void IMULogger::write_header() { + _index = 0; + String header = "timestamp,acc_x,acc_y,acc_z,gyro_x,gyro_y,gyro_z,magn_x,magn_y,magn_z\n\r"; + header.toCharArray(&(_buffer[_index]), header.length()); + _index += header.length() - 1; // -1 to remove null terminator + dump_to_sd(); +} + +bool IMULogger::open_file() { + if (_opened) return true; + // find the next available file name for the recording + const String logs_dir = "Imu"; + + if (!sd_manager.exists(logs_dir)) sd_manager.mkdir(logs_dir); + + ExFile file; + ExFile dir = sd_manager.sd->open(logs_dir); + + char fileName[64]; + char * split; + + int n = 1; + + // find highest Recording number + while (file = dir.openNextFile()) { + file.getName(fileName, sizeof(fileName)); + + split = strtok(fileName, "_"); + if (strcmp(split,"Imu") == 0) { + split = strtok(NULL, "_"); + n = max(n, atoi(split) + 1); + } + } + + // file name of the new recording + _name = "/" + logs_dir + "/Imu_" + String(n) + "_" + String(millis()) + ".csv"; + + if (_imu_debug) { + _imu_debug->println("Log filename:"); + _imu_debug->println(_name); + } + + _file = sd_manager.openFile(_name, true); + _opened = _file.isOpen(); + return _opened; +} + + + + + + diff --git a/src/sd_logger/IMU_Logger.h b/src/sd_logger/IMU_Logger.h new file mode 100644 index 0000000..560087c --- /dev/null +++ b/src/sd_logger/IMU_Logger.h @@ -0,0 +1,36 @@ +#ifndef OPEN_EARABLE_IMU_LOGGER_H +#define OPEN_EARABLE_IMU_LOGGER_H + +#include "utils/SDManager.h" +#include "EdgeML_Custom.h" +#include +#include + +// #define LOGGER_BUFFER_SIZE 1024 +#define LOGGER_BUFFER_SIZE 2048 + +class IMULogger{ +public: + static bool begin(); + static void end(); + + static void set_name(String name); + static void debug(Stream &stream); + + static void data_callback(int, unsigned int, const String&); + static void config_callback(SensorConfigurationPacket * config); +private: + static ExFatFile _file; + static bool _opened; + + static int _index; + static char _buffer[LOGGER_BUFFER_SIZE]; + + static String _name; + + static void dump_to_sd(); + static void write_header(); + static bool open_file(); +}; + +#endif //OPEN_EARABLE_IMU_LOGGER_H diff --git a/src/sd_logger/SD_Logger.cpp b/src/sd_logger/SD_Logger.cpp index 8703d83..4b4cb18 100644 --- a/src/sd_logger/SD_Logger.cpp +++ b/src/sd_logger/SD_Logger.cpp @@ -7,16 +7,22 @@ bool SD_Logger::_opened = false; char SD_Logger::_buffer[LOGGER_BUFFER_SIZE]; int SD_Logger::_index = 0; String SD_Logger::_name = "Log.csv"; +Stream * _debug{}; bool SD_Logger::begin() { _index = 0; if(!sd_manager.begin()) return false; - sd_manager.remove(_name); + if (_debug) _debug->println("Initialising file"); if (!open_file()) return false; write_header(); return _file.isOpen(); } +void SD_Logger::debug(Stream &stream) { + _debug = &stream; + _debug->println("SDLogger debug set correctly!"); +} + void SD_Logger::end() { //sd_manager.end(); } @@ -65,6 +71,38 @@ void SD_Logger::write_header() { bool SD_Logger::open_file() { if (_opened) return true; + // find the next available file name for the recording + const String logs_dir = "Logs"; + + if (!sd_manager.exists(logs_dir)) sd_manager.mkdir(logs_dir); + + ExFile file; + ExFile dir = sd_manager.sd->open(logs_dir); + + char fileName[64]; + char * split; + + int n = 1; + + // find highest Recording number + while (file = dir.openNextFile()) { + file.getName(fileName, sizeof(fileName)); + + split = strtok(fileName, "_"); + if (strcmp(split,"Log") == 0) { + split = strtok(NULL, "_"); + n = max(n, atoi(split) + 1); + } + } + + // file name of the new recording + _name = "/" + logs_dir + "/Log_" + String(n) + "_" + String(millis()) + ".csv"; + + if (_debug) { + _debug->println("Log filename:"); + _debug->println(_name); + } + _file = sd_manager.openFile(_name, true); _opened = _file.isOpen(); return _opened; diff --git a/src/sd_logger/SD_Logger.h b/src/sd_logger/SD_Logger.h index ae76944..ddba4ea 100644 --- a/src/sd_logger/SD_Logger.h +++ b/src/sd_logger/SD_Logger.h @@ -3,16 +3,18 @@ #include "utils/SDManager.h" #include "EdgeML_Custom.h" +#include // #define LOGGER_BUFFER_SIZE 1024 #define LOGGER_BUFFER_SIZE 2048 -class SD_Logger { +class SD_Logger{ public: static bool begin(); static void end(); static void set_name(String name); + static void debug(Stream &stream); static void data_callback(int, unsigned int, const String&); private: diff --git a/src/task_manager/TaskManager.cpp b/src/task_manager/TaskManager.cpp index f714b22..ee0e62d 100644 --- a/src/task_manager/TaskManager.cpp +++ b/src/task_manager/TaskManager.cpp @@ -72,21 +72,19 @@ void TaskManager::update() { void TaskManager::update_edge_ml() { unsigned int now = millis(); - if (now - _edge_ml_last >= _edge_ml_delay) { - //Serial.print("delay: "); - //Serial.println(now - _edge_ml_last - _edge_ml_delay); - - //edge_ml_generic.update(true); - sensorProvider.update_manager(); + if (_baro_delay > 0){ if (now - _baro_last >= _baro_delay) { sensorProvider.update_sensor(BARO_TEMP, true); _baro_last = now; } + } + if (_imu_delay > 0) { if (now - _imu_last >= _imu_delay) { sensorProvider.update_sensor(ACC_GYRO_MAG, true); _imu_last = now; } - + } + if (now - _edge_ml_last >= _edge_ml_delay) { bleHandler_G.update(); //Serial.print(" edge_ml time: "); //Serial.println(millis() - now); @@ -94,9 +92,8 @@ void TaskManager::update_edge_ml() { _edge_ml_last = now; _buffer_flag = false; //now = millis(); + // BLE.poll(); } - - BLE.poll(); } int TaskManager::update_audio(Provider * provider, int max_buffers) { @@ -115,8 +112,6 @@ void TaskManager::begin(float baro_samplerate, float imu_samplerate) { if (baro_samplerate >= 0) _baro_delay = (int) baro_samplerate > 0 ? (1000.0/baro_samplerate) : _default_loop_delay; if (imu_samplerate >= 0) _imu_delay = (int) imu_samplerate > 0 ? (1000.0/imu_samplerate) : _default_loop_delay; - _edge_ml_delay = min(_baro_delay, _imu_delay); - /*float edge_rate = max(baro_samplerate, imu_samplerate); if (edge_rate <= 0) { diff --git a/src/task_manager/TaskManager.h b/src/task_manager/TaskManager.h index 67ae475..3740e0d 100644 --- a/src/task_manager/TaskManager.h +++ b/src/task_manager/TaskManager.h @@ -13,13 +13,13 @@ class TaskManager { private: int _current_conf_num; - const int _default_loop_delay = 50; + const int _default_loop_delay = -1; int _baro_delay = _default_loop_delay; int _imu_delay = _default_loop_delay; //float _rate_factor = 1.25; - int _edge_ml_delay; + int _edge_ml_delay = 20; unsigned int _edge_ml_last; unsigned int _baro_last; unsigned int _imu_last;