Skip to content

Feat ble prototype example#331

Draft
Joseph-Jacobson wants to merge 26 commits intodevfrom
feat-blePrototype-Example
Draft

Feat ble prototype example#331
Joseph-Jacobson wants to merge 26 commits intodevfrom
feat-blePrototype-Example

Conversation

@Joseph-Jacobson
Copy link
Contributor

@Joseph-Jacobson Joseph-Jacobson commented Jul 23, 2025

Description

This PR enables Bluetooth Low Energy (BLE) on the EmotiBit, adding the following functionality:

  • Setup button press to switch the EmotiBit into BLE mode
  • Sending EmotiBit sensor data over UART
  • Receiving Control Packets
  • Wireless Off

ARDUINO NOTE: If you are on Arduino, please set the partition table to Huge App

NOTE: This build does NOT have time syncing enabled so all data received does not account for start time/adjustments due to latency.

This PR allows users to press the side button on start up (same time frame to get into the debug mode) to switch the EmotiBit into bluetooth mode, which is signified by a faster blinking light. A synopsis of each change is outlined in the notes for reviewer. This code was built out with this arduino example here as an example.

Requirements

Issues Referenced

None

Documentation update

None - we are not publicly releasing this PR

Notes for Reviewer

EmotiBitBluetooth.h/.cpp

This is where all the bluetooth specific functions will live and was built with EmotiBitWifi as a blueprint.

  • Begin: facilitates the bluetooth stack creation
  • Callbacks: Handles connection/disconnections/data sent
  • Send data: Handles sending data
  • Read control: Handles received data
  • Reconnect: Reestabilishes advertising

EmotiBit.h/.cpp

  • Set up: Button press during 2 second window switches the EmotiBit into bluetooth mode (same point that we get into debugger)
  • ParseIncomingControlPackets: We use bluetooth's readControl, which functions like WiFi's readControl
  • LED set up: Sets bluetooth lights to blink twice as fast
  • Wireless Off: Added provisions so wireless off works with bluetooth. NOTE: we do not entirely disable bluetooth for this mode due to deinitialization bugs. This results in a decrease in wireless off battery life compared to wifi's wireless off.

EmotiBit_stock_firemware.ino

  • onShortButtonPress edited to handle both bluetooth and wifi's wireless off modes.

board_feather_esp32.ini

  • Added huge_app.csv

Setup Flow

Setup drawio (2)

Bluetooth Connections Flow

Untitled Diagram drawio (2)

LED Speeds

Mode Speed
WiFi a1ux42
Bluetooth a1uxfl

Testing

All tests were run with Bluefruit Connect or nRF Connect which can be downloaded on the app store.

Results

Testing included the following feature tests. I also ensured that record begin/end would work as well.

Feature Tests

Feature Test Result Notes
Mock Data Testing ✔️
Packet Fixed Length Test ✔️
Recording Runtime Bluetooth ✔️ ~4.6 Hours
Wireless Off Recording Runtime Bluetooth ✔️ ~6.4 Hours
Record Begin over UART Bluetooth ✔️
Record End over UART Bluetooth ✔️
Setup EmotiBit in Bluetooth mode ✔️

Shared files

Checklist to allow merge

  • All dependent repositories used were on branch master
  • Firmware
    • Set testingMode to TestingMode::NONE
    • Set const bool DIGITAL_WRITE_DEBUG = false (if set true while testing)
    • Update version in EmotiBit.h
    • Update library.properties to the correct version (should match EmotiBit.h)
  • doxygen style comments included for new code snippets
  • Required documentation updated

Screenshots:

Summary by CodeRabbit

Release Notes

New Features

  • Added Bluetooth connectivity as an alternative wireless option for compatible devices
  • Introduced new BLUETOOTH power mode for wireless sensor data transmission
  • Real-time Bluetooth connection status with LED indicators
  • Button now toggles between Bluetooth and WiFi power modes
  • Device status (battery level, recording state) automatically reflected in Bluetooth connection state

@coderabbitai
Copy link

coderabbitai bot commented Sep 13, 2025

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

This pull request introduces Bluetooth Low Energy (BLE) support for EmotiBit on Arduino Feather ESP32 boards. New files EmotiBitBluetooth.h and EmotiBitBluetooth.cpp implement a complete BLE communication stack with server initialization, data transmission via characteristics, and connection lifecycle management. EmotiBit.h adds a new BLUETOOTH power mode and Bluetooth-related member variables. EmotiBit.cpp integrates Bluetooth initialization, control packet parsing from both WiFi and Bluetooth sources, conditional data transmission paths, LED status indicators, and power-mode transitions. The firmware and board configuration files are updated to support Bluetooth mode toggling and ESP32 partition allocation.

Possibly related PRs

  • Feat splitter refactor #329: Addresses sendData and data-transport/splitting logic refactoring that aligns with the multi-transport dispatch mechanism introduced for Bluetooth support in this PR.

Pre-merge checks

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 13.64% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title Check ✅ Passed The title "Feat ble prototype example" is partially related to the changeset. It clearly references BLE (Bluetooth Low Energy), which is the core feature being implemented across multiple files including new EmotiBitBluetooth classes, integrated control packet parsing, power mode transitions, and LED indicators. However, the terms "prototype" and "example" are somewhat misleading given that the PR description explicitly characterizes this as a full, tested implementation with comprehensive features like power mode management, wireless off support, and production considerations such as partition table configuration. The title refers to a real and central aspect of the change but undersells the actual scope and integration level of the implementation.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

Tip

📝 Customizable high-level summaries are now available in beta!

You can now customize how CodeRabbit generates the high-level summary in your pull requests — including its content, structure, tone, and formatting.

  • Provide your own instructions using the high_level_summary_instructions setting.
  • Format the summary however you like (bullet lists, tables, multi-section layouts, contributor stats, etc.).
  • Use high_level_summary_in_walkthrough to move the summary from the description to the walkthrough section.

Example instruction:

"Divide the high-level summary into five sections:

  1. 📝 Description — Summarize the main change in 50–60 words, explaining what was done.
  2. 📓 References — List relevant issues, discussions, documentation, or related PRs.
  3. 📦 Dependencies & Requirements — Mention any new/updated dependencies, environment variable changes, or configuration updates.
  4. 📊 Contributor Summary — Include a Markdown table showing contributions:
    | Contributor | Lines Added | Lines Removed | Files Changed |
  5. ✔️ Additional Notes — Add any extra reviewer context.
    Keep each section concise (under 200 words) and use bullet or numbered lists for clarity."

Note: This feature is currently in beta for Pro-tier users, and pricing will be announced later.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@Joseph-Jacobson
Copy link
Contributor Author

@coderabbitai full review

@coderabbitai
Copy link

coderabbitai bot commented Oct 23, 2025

✅ Actions performed

Full review triggered.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 21

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (4)
EmotiBit.cpp (3)

4045-4050: sendModePacket() always sends over WiFi, ignoring BLUETOOTH mode.

When in BLE mode, this won’t reach the client and unnecessarily wakes WiFi code paths.

 void EmotiBit::sendModePacket(String &sentModePacket, uint16_t &packetNumber)
 {
   createModePacket(sentModePacket, packetNumber);
-  // ToDo: This should probably be over TCP in response to specific messages from Host (but will require writing TCP ingest)
-  _emotiBitWiFi.sendData(sentModePacket); // Send packet immediately to update host
+  // Send over the active transport
+  if (getPowerMode() == PowerMode::BLUETOOTH) {
+  #ifdef ARDUINO_FEATHER_ESP32
+    _emotiBitBluetooth.sendData(sentModePacket);
+  #endif
+  } else {
+    _emotiBitWiFi.sendData(sentModePacket);
+  }
   _outDataPackets += sentModePacket;      // Add packet to slower data logging bucket
 }

2261-2273: Bitwise precedence bug causes incorrect overflow detection.

status & BufferFloat::ERROR_BUFFER_OVERFLOW == ... is parsed as status & (A==A). You’re effectively testing LSB of status.

-    if (status & BufferFloat::ERROR_BUFFER_OVERFLOW == BufferFloat::ERROR_BUFFER_OVERFLOW) {
+    if ((status & BufferFloat::ERROR_BUFFER_OVERFLOW) == BufferFloat::ERROR_BUFFER_OVERFLOW) {

This affects reliability of overflow handling.


3995-4042: Add BLUETOOTH power mode case to createModePacket() using existing MODE_BLUETOOTH.

The BLUETOOTH power mode case is missing from createModePacket(). EmotiBitPacket::TypeTag::MODE_BLUETOOTH already exists in the codebase and is actively used elsewhere (e.g., line 1601), so use it directly without a temporary string placeholder.

   else if(getPowerMode() == PowerMode::WIRELESS_OFF)
   {
     payload += EmotiBitPacket::TypeTag::MODE_WIRELESS_OFF;
   }
+  else if (getPowerMode() == PowerMode::BLUETOOTH)
+  {
+    payload += EmotiBitPacket::TypeTag::MODE_BLUETOOTH;
+  }
   else if (getPowerMode() == PowerMode::HIBERNATE)
EmotiBit.h (1)

266-274: Add PowerMode::BLUETOOTH handling to createModePacket().

The createModePacket() function (EmotiBit.cpp:3994-4042) handles NORMAL_POWER, LOW_POWER, MAX_LOW_POWER, WIRELESS_OFF, and HIBERNATE modes (lines 4017-4036), but lacks a case for PowerMode::BLUETOOTH. When the device is in BLUETOOTH mode, no MODE_BLUETOOTH tag is emitted, causing the host/UI to be unaware of the actual power state. Additionally, sendModePacket() (line 4048) always transmits via WiFi regardless of mode, which won't reach clients connected only over Bluetooth.

Add this condition in createModePacket() before the final dataCount++:

else if (getPowerMode() == PowerMode::BLUETOOTH)
{
	payload += EmotiBitPacket::TypeTag::MODE_BLUETOOTH;
}

Also review sendModePacket() to ensure Bluetooth-connected clients receive mode updates (the ToDo comment at line 4047 hints at this broader design need).

📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 441fbfc and 176cb58.

📒 Files selected for processing (6)
  • EmotiBit.cpp (10 hunks)
  • EmotiBit.h (5 hunks)
  • EmotiBitBluetooth.cpp (1 hunks)
  • EmotiBitBluetooth.h (1 hunks)
  • EmotiBit_stock_firmware/EmotiBit_stock_firmware.ino (1 hunks)
  • board_feather_esp32.ini (1 hunks)
🧰 Additional context used
🪛 Clang (14.0.6)
EmotiBit.cpp

[note] 960-960: +1, including nesting penalty of 0, nesting level increased to 1

(clang)


[note] 967-967: +1, nesting level increased to 1

(clang)


[warning] 960-960: redundant boolean literal supplied to boolean operator

(readability-simplify-boolean-expr)


[note] 1525-1525: +1, including nesting penalty of 0, nesting level increased to 1

(clang)


[note] 1601-1601: +1, nesting level increased to 3

(clang)


[note] 3227-3227: +5, including nesting penalty of 4, nesting level increased to 5

(clang)


[warning] 3860-3860: if with identical then and else branches

(bugprone-branch-clone)


[note] 3867-3867: else branch starts here

(clang)


[warning] 3860-3860: redundant boolean literal supplied to boolean operator

(readability-simplify-boolean-expr)


[warning] 3873-3873: repeated branch in conditional chain

(bugprone-branch-clone)


[note] 4452-4452: +1, nesting level increased to 2

(clang)


[note] 4451-4451: end of the original

(clang)


[note] 4453-4453: clone 1 starts here

(clang)

🪛 GitHub Actions: Build EmotiBit FeatherWing Firmware
EmotiBit.cpp

[error] 1601-1601: EmotiBitPacket::TypeTag does not contain MODE_BLUETOOTH. Compilation failed due to invalid enum value usage.

🔇 Additional comments (3)
board_feather_esp32.ini (1)

11-12: Partition change: confirm trade-offs.

huge_app.csv reduces SPIFFS/OTA space. Verify this is acceptable for your SD/FTP flows and that no OTA or on-device FS is relied upon elsewhere.

EmotiBit_stock_firmware/EmotiBit_stock_firmware.ino (1)

13-24: BLE/Wireless Off toggle logic LGTM.

The button toggles between BLUETOOTH and WIRELESS_OFF when Bluetooth is enabled; otherwise WiFi path toggles as before. Clear and consistent.

EmotiBitBluetooth.cpp (1)

31-33: I'm unable to locate EmotiBitBluetooth.cpp in the public repositories despite multiple attempts. Without access to the actual codebase, I cannot verify:

  1. Which BLE stack is in use (ESP32 Arduino Bluedroid vs NimBLE-Arduino)
  2. The current implementation at lines 31-33 and 112-121
  3. Whether security infrastructure is already configured elsewhere
  4. Whether the MyCallbacks implementation properly handles security

However, the web search results confirm that the review comment's suggested API patterns are accurate:

  • For ESP32 Arduino (Bluedroid): Use setAccessPermissions(ESP_GATT_PERM_WRITE_ENCRYPTED) or setAccessPermissions(ESP_GATT_PERM_WRITE_ENC_MITM) after creating the characteristic
  • For NimBLE-Arduino: Use NIMBLE_PROPERTY::WRITE_ENC when calling createCharacteristic()

Both approaches require security/pairing infrastructure to be enabled on the device.

The security concern raised in the review comment is valid — writable BLE characteristics should require encryption and ideally MITM authentication to prevent unauthorized control writes.

Comment on lines +163 to +170
void EmotiBitBluetooth::end()
{
if (pServer && deviceConnected)
{
//tear down the old connection
pServer->disconnect(0);
Serial.println("BLE client disconnected by end()");
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Avoid hard-coded disconnect(0); track and use actual connection ID.

disconnect(0) may be wrong if the connId isn’t 0; store connId on connect and use it on end().

Search for an available API to retrieve the connection id:

If supported, in onConnect store server->_connId = pServer->getConnId(); and in end() call pServer->disconnect(server->_connId);. Otherwise, fall back to a parameterless disconnect if available.

Also applies to: 43-47


🏁 Script executed:

#!/bin/bash
rg -nP --type=cpp 'getConnId|disconnect\s*\(' -C2

Length of output: 3051


I need to gather more information about the BLE library being used and examine the onConnect handler.

Now I need to examine the onConnect handler and verify how disconnect() is being called in the codebase.


🏁 Script executed:

#!/bin/bash
# Find the onConnect callback and surrounding context (lines 43-47)
rg -nA15 'onConnect\s*\(' EmotiBitBluetooth.cpp | head -40

Length of output: 663



🏁 Script executed:

#!/bin/bash
# Find EmotiBitBluetooth class definition and member variables
rg -nB5 'class EmotiBitBluetooth' EmotiBitBluetooth.h

Length of output: 191


🏁 Script executed:

#!/bin/bash
# Look for member variables and initialization in header file
rg -nP 'uint\d+_t|int\d+_t|bool|connId' EmotiBitBluetooth.h | head -30

Length of output: 298


🏁 Script executed:

#!/bin/bash
# Check if getConnId is available in any BLE-related headers or code
rg -n 'getConnId|connId|connHandle' EmotiBitBluetooth.cpp EmotiBitBluetooth.h

Length of output: 0



🏁 Script executed:

#!/bin/bash
# Find BLE library includes and setup code
rg -n '#include.*BLE' EmotiBitBluetooth.cpp | head -20

Length of output: 0


🏁 Script executed:

#!/bin/bash
# Search for BLEServer initialization and setup
rg -nB3A3 'pServer.*=.*new.*BLEServer|BLEServer::createServer|pServer->setCallbacks' EmotiBitBluetooth.cpp | head -40

Length of output: 81


🏁 Script executed:

#!/bin/bash
# Look for disconnect method calls to understand the expected signature
rg -nB2A2 'pServer->disconnect' EmotiBitBluetooth.cpp

Length of output: 81


Copy link
Collaborator

@produceconsumerobot produceconsumerobot left a comment

Choose a reason for hiding this comment

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

I think our future selves may be significantly less disappointed in our present selves if we refactor setPowerMode to correctly handle BT. I've made suggested changes in the branch feat-blePrototype-Example-fix-setPowerMode

Make sure to highlight in your PR which data integrity tests have been run on SD card data using wifi (normal use-case) in addition to BLE situations.

Please also specify in the PR the battery life when using wifi (and wifi->wireless off) modes.

static String packet;
static EmotiBitPacket::Header header;
int16_t dataStartChar = 0;
#ifdef ARDUINO_FEATHER_ESP32
Copy link
Collaborator

Choose a reason for hiding this comment

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

Could we remove this #ifdef and return zero from _emotiBitBluetooth.readControl(packet)?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The entire bluetooth file is wrapped in an indef so the app will build on m0, it would break on m0.

EmotiBit.cpp Outdated
}

if (getPowerMode() == PowerMode::BLUETOOTH) {
#ifdef ARDUINO_FEATHER_ESP32
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can we remove this #ifdef ARDUINO_FEATHER_ESP32?
If we got here, can't we just assume it's a BT compatible board?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removing the ifdefs here breaks m0

EmotiBit.cpp Outdated
// Battery LED
if (battIndicationSeq)
{
led.setState(EmotiBitLedController::Led::YELLOW, true);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why do we need to have the Led::YELLOW and Led::RED controls duplicated for PowerMode::BLUETOOTH and not? Seems like one source of truth could be enough.

_emotiBitWiFi.sendData(s);
}
if (getPowerMode() == PowerMode::BLUETOOTH) {
#ifdef ARDUINO_FEATHER_ESP32
Copy link
Collaborator

Choose a reason for hiding this comment

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

Could we get rid of #ifdef ARDUINO_FEATHER_ESP32 here and rely on the fact that we've already checked for BT compatibility before entering PowerMode::BLUETOOTH?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This would also lead to m0 breaking

#include <SD.h>
#include "driver/adc.h"
#include <esp_bt.h>
#include <esp_bt.h> //consider moving into bluetooth
Copy link
Collaborator

Choose a reason for hiding this comment

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

How about creating EmotiBitBluetooth::disable() and moving #include <esp_bt.h> into EmotiBitBluetooth to avoid littering the place with bluetooth code?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

3 participants