An Arduino library for the Hublink BEAM ESP32-S3 data logging device. This library provides functionality for sensor monitoring and SD card data logging.
-
In Arduino IDE, you use Boards Manager to install the Espressif
esp32board package. -
Install the Hublink-BEAM library from Arduino IDE. Alternatively (but not recommended), clone or download this repository to the libraries folder in the Arduino IDE, (or use Sketch -> Include Library -> Add .ZIP Library).
-
Ensure the dependencies are installed for library.properties and https://github.com/Neurotech-Hub/Hublink-Node:
- RTClib
- Adafruit MAX1704X
- Adafruit BME280 Library
- Adafruit VEML7700 Library
- Adafruit NeoPixel
- ESP32Time
- Preferences
meta.json File Contents
BEAM must have a meta.json file present to control timing parameters (if deviating from the sketch defaults) and sync with Hublink. Several Hublink parameters are disregarded because of BEAM's deep sleep cycling: advertise_every, advertise_for, and reconnect settings are not utilized but can still be placed in the hublink structure.
json
{
"hublink": {
"advertise": "HUBLINK",
"advertise_every": 120,
"advertise_for": 30,
"try_reconnect": false,
"reconnect_attempts": 2,
"reconnect_every": 30,
"upload_path": "/BEAM",
"append_path": "device:id",
"disable": false
},
"beam": {
"log_every_minutes": 1,
"sync_every_minutes": 3,
"sync_for_seconds": 30,
"new_file_on_boot": true,
"inactivity_period_seconds": 40,
"randomize_alarm_minutes": 1
},
"subject": {
"id": "",
"strain": "",
"sex": "F"
},
"experiment": {
"name": ""
},
"device": {
"id": "001"
}
}
Testing
-
Ensure the power switch is in the
ONposition. -
If the device has been previously flashed, it may be in a deep sleep state and will not connect to the serial port. To enter boot mode, hold the
Bootbutton and toggle theResetbutton (then release theBootbutton). -
If the Arduino IDE does not indicate that it is connected to "Adafruit Feather ESP32-S3 2MB PSRAM", click Tools -> Board -> esp32 -> Adafruit Feather ESP32-S3 2MB PSRAM (you will need to download the esp32 board package (by espressif) from the Arduino IDE).
You may enable debugging mode by placing the A switch down. This will reduce the PIR sensor initialization period and introduce delays before Serial statements so they can be read by a serial terminal. These delays are useful for debugging, but should be removed for normal operation.
Setting the RTC is performed using the compilation time of the sketch. This is not always accurate and typically requires a clearing of your sketch cache to ensure correctness.
Clear Arduino IDE Cache:
On MacOS:
rm -rf /Users/<username>/Library/Caches/arduino/sketchesOn Windows:
rmdir /s "%LOCALAPPDATA%\Arduino15\packages\esp32\tools\esptool_py\*\cache"
del /s /q "%TEMP%\arduino_*"On Linux:
rm -rf ~/.arduino15/packages/esp32/tools/esptool_py/*/cache
rm -rf /tmp/arduino_*Alternative RTC Setting Methods:
-
Manual RTC Setting (more accurate):
// Set RTC to specific date/time beam.adjustRTC(DateTime(2024, 1, 15, 10, 30, 0)); // Year, Month, Day, Hour, Minute, Second
-
Unix Timestamp Setting:
// Set RTC using Unix timestamp beam.adjustRTC(1705315800); // Unix timestamp
-
Ensure Fresh Compilation:
- Close Arduino IDE completely
- Clear cache as shown above
- Reopen IDE and compile/upload immediately for most accurate compilation time
For detailed hardware assembly instructions, including the required low-power modification, see docs/assembly.md.
These indicators may be temporary during operations or flash continuously if stuck in a loop.
- Blue: Starting up
- Red: Error (SD card or sensor failure)
- Green: Motion detected (during data logging)
- Purple: Low battery
- White: Hublink sync on boot (attempt is ongoing)
- Off: Normal operation
The device implements intelligent battery protection to prevent over-discharge while maintaining operational reliability:
Low Battery Threshold: 3.7V
First Boot/Power-On:
- Battery < 3.7V: Device will not start (purple LED, initialization failure)
- Protects battery from deep discharge damage
- Switch A down (debug mode): Bypasses protection for development/testing
Wake from Deep Sleep:
- Battery < 3.7V: Device continues operation (logs low battery state)
- Ensures continuous data collection even if battery drops during use
- Allows device to complete current logging cycle and save data
Benefits:
- Prevents battery damage from over-discharge on startup
- Maintains data integrity during normal operation
- Clear visual indicators (purple LED) for low battery state
- Debug override available for development work
Each log entry contains the following columns:
datetime: Current date and time (YYYY-MM-DD HH:MM:SS)millis: Milliseconds since last bootdevice_id: Device identifier (3-character alphanumeric)library_version: Library version stringbattery_voltage: Battery voltage in volts (-1.0 if sensor failed)temperature_c: Temperature in Celsius (-273.15 if sensor failed)pressure_hpa: Atmospheric pressure in hPa (-1.0 if sensor failed)humidity_percent: Relative humidity percentage (-1.0 if sensor failed)lux: Light level in lux (-1.0 if sensor failed)activity_count: Number of motion events since last logactivity_percent: Fraction of time PIR was active (0-1)inactivity_period_s: Configured inactivity period in secondsinactivity_count: Number of complete inactivity periodsinactivity_percent: Fraction of possible inactivity periods (0-1)min_free_heap: Minimum free heap memory in bytesreboot: 1 if entry is from fresh boot, 0 if from wake from sleep
Files are named in the format /BEAM_YYYYMMDDXX.csv where:
YYYY: YearMM: Month (01-12)DD: Day (01-31)XX: Sequence number (00-99)
For a detailed flowchart of the filename selection logic, see docs/filename_logic.md.
The sequence number handling can be controlled with the setNewFileOnBoot() method:
beam.setNewFileOnBoot(false); // false to continue using same file if it's the same day- When
true(default): Creates a new file with incremented sequence number on each boot - When
false: Continues using the same file if it's from the same day
If the SD card is cleared or files are deleted:
- The system scans for existing files matching today's date
- Uses the next available sequence number (00-99)
- Creates a new file regardless of any stored filename in preferences
- This ensures proper sequencing even after data is wiped
On power-up or reset, the device:
- Stops any running ULP program to free GPIO pins
- Initializes pins and I2C power
- Checks battery voltage (purple LED and initialization failure if battery < 3.7V on first boot)
- Initializes SD card (must be present)
- Sets up sensors and RTC
- Creates a new log file with today's date and next available number
The device uses the ESP32's ULP (Ultra Low Power) coprocessor to monitor motion while the main processor is in deep sleep. See docs/flowchart.md for a detailed flow diagram.
The inactivity period can be set using the setInactivityPeriod() method:
beam.setInactivityPeriod(40); // 40 seconds of immobility indicates sleepThe 40-second threshold is based on research by Brown et al. (2017) which demonstrated that extended immobility of >40 seconds provides a reliable indicator of sleep, correlating well with EEG-defined sleep (Pearson's r >0.95, n=4 mice).
To prevent multiple devices from syncing simultaneously (which can cause network collisions), the library supports alarm randomization:
beam.setAlarmRandomization(1); // Randomize alarm timing by ±1 minuteConfiguration via meta.json:
{
"beam": {
"randomize_alarm_minutes": 1
}
}How it works:
- Each device generates a unique, consistent delay based on its MAC address
- Random delay ranges from 0 to (2 ×
randomize_alarm_minutes) minutes - Applied during device initialization after successful sensor setup
- Same device always uses the same delay (deterministic based on hardware)
- Default value of 0 disables randomization (backward compatible)
- Debug mode (Switch A down) skips the delay for faster development
Example with randomize_alarm_minutes: 1:
- Device A (MAC: AA:BB:CC...): Always delays 23 seconds on boot
- Device B (MAC: 11:22:33...): Always delays 47 seconds on boot
- Device C (MAC: FF:EE:DD...): Always delays 8 seconds on boot
This spreads sync operations across a 0-2 minute window, eliminating collisions when multiple devices wake simultaneously.
- ULP program continuously monitors the PIR sensor
- Increments PIR count when motion is detected
- Tracks inactivity periods based on configured duration
- Maintains counters in RTC memory
- Main processor reads counters upon waking
- Configures ULP program with current settings
- Disables sensors and peripherals to save power
- Enters deep sleep for specified duration
- Wakes on timer expiration to log data
- Calculates activity metrics upon wake
See the examples folder for detailed usage examples:
- BasicLogging: Demonstrates basic data logging functionality
- Idle SD card by immediately checking for
/x.txt - Two-stage deep sleep for optimal power usage
- VEM7700 has low power modes (in library); reduces from ~13µA to ~5µA
- BME280 has low power modes (in library); reduces from ~650µA to ~10µA
- MAX17048 has hibernation mode (in library); reduces from ~23µA to ~4µA
- SD card draws ~1200µA; could optimize
- SD card detent pull-up could be ~60-160µA when card is present; consider toggling pull-up to check
- ZDP323 draws ~8µA (no low power mode)
- consider rtc_gpio_isolate(GPIO_NUM_12); from esp-idf/examples/system/ulp/ulp_fsm/ulp/main/ulp_example_main.c at v5.4 · espressif/esp-idf
Copyright © 2024 Neurotech Hub at Washington University in St. Louis. All rights reserved.
This software is proprietary and confidential. The source code is the property of the Neurotech Hub at Washington University in St. Louis and is protected by intellectual property laws. No part of this software may be deployed, copied, modified, or distributed in any form or by any means without the prior written permission from the Neurotech Hub at Washington University in St. Louis.
For licensing inquiries, please contact the Neurotech Hub at Washington University in St. Louis.