Skip to content

Instantly share code, notes, and snippets.

@Pastor
Created November 12, 2025 06:09
Show Gist options
  • Select an option

  • Save Pastor/72a8992e5b0a6e025a468898a5723f2e to your computer and use it in GitHub Desktop.

Select an option

Save Pastor/72a8992e5b0a6e025a468898a5723f2e to your computer and use it in GitHub Desktop.
M5 Capsule BLE
#include <M5Unified.h>
#include <M5Capsule.h>
#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEScan.h>
#include <BLEAdvertisedDevice.h>
#include <FastLED.h>
#include "EEPROM.h"
#include "FS.h"
#include "SD.h"
#include "SPI.h"
#define PIN_LED 21
#define NUM_LEDS 1
CRGB leds[NUM_LEDS];
BLEScan* scan;
bool has_sd = false;
enum ScanState {
STATE_DISABLED,
STATE_ENABLED,
STATE_MIDDLE,
STATE_FAILED
};
ScanState state = STATE_MIDDLE;
class SimpleBLEAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks {
void onResult(BLEAdvertisedDevice device) {
File fd = SD.open("/ble/scan.csv", FILE_APPEND);
if (!fd) {
state = STATE_FAILED;
return;
}
String address = device.getAddress().toString();
fd.printf("%d,%s", millis(), address.c_str());
fd.print(",");
if (device.haveRSSI()) {
fd.printf("%d", device.getRSSI());
}
fd.print(",");
if (device.haveName()) {
fd.printf("%s", device.getName().c_str());
}
fd.print(",");
if (device.haveTXPower()) {
fd.printf("%d", device.getTXPower());
}
fd.print(",");
if (device.haveServiceData()) {
String serviceData = device.getServiceData();
uint8_t cServiceData[255];
size_t dataLength = serviceData.length();
if (dataLength <= sizeof(cServiceData)) {
memcpy(cServiceData, serviceData.c_str(), dataLength);
for (int i = 0; i < dataLength; i++) {
fd.printf("%02X", cServiceData[i]);
}
}
}
fd.print(",");
if (device.haveManufacturerData()) {
String strManufacturerData = device.getManufacturerData();
uint8_t cManufacturerData[255];
size_t dataLength = strManufacturerData.length();
if (dataLength <= sizeof(cManufacturerData)) {
memcpy(cManufacturerData, strManufacturerData.c_str(), dataLength);
for (int i = 0; i < dataLength; i++) {
fd.printf("%02X", cManufacturerData[i]);
}
}
}
fd.print(",");
if (device.haveServiceUUID()) {
BLEUUID uuid = device.getServiceUUID();
if (uuid.bitSize() > 0) {
fd.printf("%s", uuid.toString().c_str());
} else {
fd.print("00000000-00000000-00000000-00000000");
}
}
fd.printf(",%d", M5Capsule.Power.getBatteryLevel());
fd.println();
fd.close();
}
};
void setLedState(ScanState state) {
switch (state) {
case STATE_DISABLED:
leds[0] = CRGB::Red;
break;
case STATE_ENABLED:
leds[0] = CRGB::Blue;
break;
case STATE_MIDDLE:
leds[0] = CRGB::Green;
break;
case STATE_FAILED:
leds[0] = CRGB::Yellow;
break;
}
FastLED.show();
}
void setup() {
Serial.begin(115200);
auto cfg = M5.config();
M5Capsule.begin(cfg);
FastLED.addLeds<WS2812, PIN_LED, GRB>(leds, NUM_LEDS);
setLedState(state);
has_sd = SD.begin();
if (has_sd) {
if (!SD.exists("/ble/scan.csv")) {
SD.mkdir("/ble");
File fd = SD.open("/ble/scan.csv", FILE_WRITE);
if (fd) {
fd.println("mills,address,rssi,name,tx,service_data,manufacture_data,service_uuid,battery");
fd.close();
}
}
}
BLEDevice::init("");
scan = BLEDevice::getScan();
scan->setAdvertisedDeviceCallbacks(new SimpleBLEAdvertisedDeviceCallbacks());
scan->setActiveScan(true);
scan->setInterval(100);
scan->setWindow(99);
delay(1000);
state = STATE_DISABLED;
setLedState(state);
M5.Speaker.begin();
M5.Speaker.stop();
}
void loop() {
M5Capsule.update();
if (M5Capsule.BtnA.wasClicked()) {
if (state == STATE_DISABLED) {
state = STATE_ENABLED;
} else if (state == STATE_ENABLED || state == STATE_FAILED) {
state = STATE_DISABLED;
scan->clearResults();
//M5Capsule.Power.powerOff();
}
setLedState(state);
}
if (state == STATE_ENABLED) {
setLedState(STATE_ENABLED);
BLEScanResults* foundDevices = scan->start(5, false);
scan->clearResults();
setLedState(STATE_MIDDLE);
delay(1000);
if (M5Capsule.Power.getBatteryLevel() < 20) {
M5.Speaker.tone(1000, 100);
delay(1000);
M5.Speaker.stop();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment