mirror of
https://github.com/meshtastic/firmware.git
synced 2025-04-25 01:42:15 +00:00

* Move static functions into Screen.h, show compass during calibration * Move to _fontHeight macro to avoid collision * Move some alert functions to new alert handler * Catch missed reboot code * ESP32 fixes * Bump esp8266-oled-ssd1306 * Fixes for when a device has no screen * Use new startAlert(char*) helper class * Add EINK bits back to alert handling * Add noop class for no-display devices --------- Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
286 lines
10 KiB
C++
286 lines
10 KiB
C++
#include "configuration.h"
|
|
#if !MESHTASTIC_EXCLUDE_BLUETOOTH
|
|
#include "BluetoothCommon.h"
|
|
#include "NimbleBluetooth.h"
|
|
#include "PowerFSM.h"
|
|
|
|
#include "main.h"
|
|
#include "mesh/PhoneAPI.h"
|
|
#include "mesh/mesh-pb-constants.h"
|
|
#include "sleep.h"
|
|
#include <NimBLEDevice.h>
|
|
|
|
NimBLECharacteristic *fromNumCharacteristic;
|
|
NimBLECharacteristic *BatteryCharacteristic;
|
|
NimBLECharacteristic *logRadioCharacteristic;
|
|
NimBLEServer *bleServer;
|
|
|
|
static bool passkeyShowing;
|
|
|
|
class BluetoothPhoneAPI : public PhoneAPI
|
|
{
|
|
/**
|
|
* Subclasses can use this as a hook to provide custom notifications for their transport (i.e. bluetooth notifies)
|
|
*/
|
|
virtual void onNowHasData(uint32_t fromRadioNum)
|
|
{
|
|
PhoneAPI::onNowHasData(fromRadioNum);
|
|
|
|
LOG_INFO("BLE notify fromNum\n");
|
|
|
|
uint8_t val[4];
|
|
put_le32(val, fromRadioNum);
|
|
|
|
fromNumCharacteristic->setValue(val, sizeof(val));
|
|
fromNumCharacteristic->notify();
|
|
}
|
|
|
|
/// Check the current underlying physical link to see if the client is currently connected
|
|
virtual bool checkIsConnected() { return bleServer && bleServer->getConnectedCount() > 0; }
|
|
};
|
|
|
|
static BluetoothPhoneAPI *bluetoothPhoneAPI;
|
|
/**
|
|
* Subclasses can use this as a hook to provide custom notifications for their transport (i.e. bluetooth notifies)
|
|
*/
|
|
|
|
class NimbleBluetoothToRadioCallback : public NimBLECharacteristicCallbacks
|
|
{
|
|
virtual void onWrite(NimBLECharacteristic *pCharacteristic)
|
|
{
|
|
LOG_INFO("To Radio onwrite\n");
|
|
auto val = pCharacteristic->getValue();
|
|
|
|
bluetoothPhoneAPI->handleToRadio(val.data(), val.length());
|
|
}
|
|
};
|
|
|
|
class NimbleBluetoothFromRadioCallback : public NimBLECharacteristicCallbacks
|
|
{
|
|
virtual void onRead(NimBLECharacteristic *pCharacteristic)
|
|
{
|
|
uint8_t fromRadioBytes[meshtastic_FromRadio_size];
|
|
size_t numBytes = bluetoothPhoneAPI->getFromRadio(fromRadioBytes);
|
|
|
|
std::string fromRadioByteString(fromRadioBytes, fromRadioBytes + numBytes);
|
|
|
|
pCharacteristic->setValue(fromRadioByteString);
|
|
}
|
|
};
|
|
|
|
class NimbleBluetoothServerCallback : public NimBLEServerCallbacks
|
|
{
|
|
virtual uint32_t onPassKeyRequest()
|
|
{
|
|
uint32_t passkey = config.bluetooth.fixed_pin;
|
|
|
|
if (config.bluetooth.mode == meshtastic_Config_BluetoothConfig_PairingMode_RANDOM_PIN) {
|
|
LOG_INFO("Using random passkey\n");
|
|
// This is the passkey to be entered on peer - we pick a number >100,000 to ensure 6 digits
|
|
passkey = random(100000, 999999);
|
|
}
|
|
LOG_INFO("*** Enter passkey %d on the peer side ***\n", passkey);
|
|
|
|
powerFSM.trigger(EVENT_BLUETOOTH_PAIR);
|
|
#if HAS_SCREEN
|
|
screen->startAlert([passkey](OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) -> void {
|
|
char btPIN[16] = "888888";
|
|
snprintf(btPIN, sizeof(btPIN), "%06u", passkey);
|
|
int x_offset = display->width() / 2;
|
|
int y_offset = display->height() <= 80 ? 0 : 32;
|
|
display->setTextAlignment(TEXT_ALIGN_CENTER);
|
|
display->setFont(FONT_MEDIUM);
|
|
display->drawString(x_offset + x, y_offset + y, "Bluetooth");
|
|
|
|
display->setFont(FONT_SMALL);
|
|
y_offset = display->height() == 64 ? y_offset + FONT_HEIGHT_MEDIUM - 4 : y_offset + FONT_HEIGHT_MEDIUM + 5;
|
|
display->drawString(x_offset + x, y_offset + y, "Enter this code");
|
|
|
|
display->setFont(FONT_LARGE);
|
|
String displayPin(btPIN);
|
|
String pin = displayPin.substring(0, 3) + " " + displayPin.substring(3, 6);
|
|
y_offset = display->height() == 64 ? y_offset + FONT_HEIGHT_SMALL - 5 : y_offset + FONT_HEIGHT_SMALL + 5;
|
|
display->drawString(x_offset + x, y_offset + y, pin);
|
|
|
|
display->setFont(FONT_SMALL);
|
|
String deviceName = "Name: ";
|
|
deviceName.concat(getDeviceName());
|
|
y_offset = display->height() == 64 ? y_offset + FONT_HEIGHT_LARGE - 6 : y_offset + FONT_HEIGHT_LARGE + 5;
|
|
display->drawString(x_offset + x, y_offset + y, deviceName);
|
|
});
|
|
#endif
|
|
passkeyShowing = true;
|
|
|
|
return passkey;
|
|
}
|
|
|
|
virtual void onAuthenticationComplete(ble_gap_conn_desc *desc)
|
|
{
|
|
LOG_INFO("BLE authentication complete\n");
|
|
|
|
if (passkeyShowing) {
|
|
passkeyShowing = false;
|
|
screen->endAlert();
|
|
}
|
|
}
|
|
|
|
virtual void onDisconnect(NimBLEServer *pServer, ble_gap_conn_desc *desc) { LOG_INFO("BLE disconnect\n"); }
|
|
};
|
|
|
|
static NimbleBluetoothToRadioCallback *toRadioCallbacks;
|
|
static NimbleBluetoothFromRadioCallback *fromRadioCallbacks;
|
|
|
|
void NimbleBluetooth::shutdown()
|
|
{
|
|
// No measurable power saving for ESP32 during light-sleep(?)
|
|
#ifndef ARCH_ESP32
|
|
// Shutdown bluetooth for minimum power draw
|
|
LOG_INFO("Disable bluetooth\n");
|
|
NimBLEAdvertising *pAdvertising = NimBLEDevice::getAdvertising();
|
|
pAdvertising->reset();
|
|
pAdvertising->stop();
|
|
#endif
|
|
}
|
|
|
|
// Proper shutdown for ESP32. Needs reboot to reverse.
|
|
void NimbleBluetooth::deinit()
|
|
{
|
|
#ifdef ARCH_ESP32
|
|
LOG_INFO("Disable bluetooth until reboot\n");
|
|
NimBLEDevice::deinit();
|
|
#endif
|
|
}
|
|
|
|
// Has initial setup been completed
|
|
bool NimbleBluetooth::isActive()
|
|
{
|
|
return bleServer;
|
|
}
|
|
|
|
bool NimbleBluetooth::isConnected()
|
|
{
|
|
return bleServer->getConnectedCount() > 0;
|
|
}
|
|
|
|
int NimbleBluetooth::getRssi()
|
|
{
|
|
if (bleServer && isConnected()) {
|
|
auto service = bleServer->getServiceByUUID(MESH_SERVICE_UUID);
|
|
uint16_t handle = service->getHandle();
|
|
return NimBLEDevice::getClientByID(handle)->getRssi();
|
|
}
|
|
return 0; // FIXME figure out where to source this
|
|
}
|
|
|
|
void NimbleBluetooth::setup()
|
|
{
|
|
// Uncomment for testing
|
|
// NimbleBluetooth::clearBonds();
|
|
|
|
LOG_INFO("Initialise the NimBLE bluetooth module\n");
|
|
|
|
NimBLEDevice::init(getDeviceName());
|
|
NimBLEDevice::setPower(ESP_PWR_LVL_P9);
|
|
|
|
if (config.bluetooth.mode != meshtastic_Config_BluetoothConfig_PairingMode_NO_PIN) {
|
|
NimBLEDevice::setSecurityAuth(BLE_SM_PAIR_AUTHREQ_BOND | BLE_SM_PAIR_AUTHREQ_MITM | BLE_SM_PAIR_AUTHREQ_SC);
|
|
NimBLEDevice::setSecurityInitKey(BLE_SM_PAIR_KEY_DIST_ENC | BLE_SM_PAIR_KEY_DIST_ID);
|
|
NimBLEDevice::setSecurityRespKey(BLE_SM_PAIR_KEY_DIST_ENC | BLE_SM_PAIR_KEY_DIST_ID);
|
|
NimBLEDevice::setSecurityIOCap(BLE_HS_IO_DISPLAY_ONLY);
|
|
}
|
|
bleServer = NimBLEDevice::createServer();
|
|
|
|
NimbleBluetoothServerCallback *serverCallbacks = new NimbleBluetoothServerCallback();
|
|
bleServer->setCallbacks(serverCallbacks, true);
|
|
setupService();
|
|
startAdvertising();
|
|
}
|
|
|
|
void NimbleBluetooth::setupService()
|
|
{
|
|
NimBLEService *bleService = bleServer->createService(MESH_SERVICE_UUID);
|
|
NimBLECharacteristic *ToRadioCharacteristic;
|
|
NimBLECharacteristic *FromRadioCharacteristic;
|
|
// Define the characteristics that the app is looking for
|
|
if (config.bluetooth.mode == meshtastic_Config_BluetoothConfig_PairingMode_NO_PIN) {
|
|
ToRadioCharacteristic = bleService->createCharacteristic(TORADIO_UUID, NIMBLE_PROPERTY::WRITE);
|
|
FromRadioCharacteristic = bleService->createCharacteristic(FROMRADIO_UUID, NIMBLE_PROPERTY::READ);
|
|
fromNumCharacteristic = bleService->createCharacteristic(FROMNUM_UUID, NIMBLE_PROPERTY::NOTIFY | NIMBLE_PROPERTY::READ);
|
|
logRadioCharacteristic =
|
|
bleService->createCharacteristic(LOGRADIO_UUID, NIMBLE_PROPERTY::NOTIFY | NIMBLE_PROPERTY::READ, 512U);
|
|
} else {
|
|
ToRadioCharacteristic = bleService->createCharacteristic(
|
|
TORADIO_UUID, NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::WRITE_AUTHEN | NIMBLE_PROPERTY::WRITE_ENC);
|
|
FromRadioCharacteristic = bleService->createCharacteristic(
|
|
FROMRADIO_UUID, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::READ_AUTHEN | NIMBLE_PROPERTY::READ_ENC);
|
|
fromNumCharacteristic =
|
|
bleService->createCharacteristic(FROMNUM_UUID, NIMBLE_PROPERTY::NOTIFY | NIMBLE_PROPERTY::READ |
|
|
NIMBLE_PROPERTY::READ_AUTHEN | NIMBLE_PROPERTY::READ_ENC);
|
|
logRadioCharacteristic = bleService->createCharacteristic(
|
|
LOGRADIO_UUID,
|
|
NIMBLE_PROPERTY::NOTIFY | NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::READ_AUTHEN | NIMBLE_PROPERTY::READ_ENC, 512U);
|
|
logRadioCharacteristic->setValue("Init");
|
|
}
|
|
bluetoothPhoneAPI = new BluetoothPhoneAPI();
|
|
|
|
toRadioCallbacks = new NimbleBluetoothToRadioCallback();
|
|
ToRadioCharacteristic->setCallbacks(toRadioCallbacks);
|
|
|
|
fromRadioCallbacks = new NimbleBluetoothFromRadioCallback();
|
|
FromRadioCharacteristic->setCallbacks(fromRadioCallbacks);
|
|
|
|
bleService->start();
|
|
|
|
// Setup the battery service
|
|
NimBLEService *batteryService = bleServer->createService(NimBLEUUID((uint16_t)0x180f)); // 0x180F is the Battery Service
|
|
BatteryCharacteristic = batteryService->createCharacteristic( // 0x2A19 is the Battery Level characteristic)
|
|
(uint16_t)0x2a19, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::NOTIFY, 1);
|
|
|
|
NimBLE2904 *batteryLevelDescriptor = (NimBLE2904 *)BatteryCharacteristic->createDescriptor((uint16_t)0x2904);
|
|
batteryLevelDescriptor->setFormat(NimBLE2904::FORMAT_UINT8);
|
|
batteryLevelDescriptor->setNamespace(1);
|
|
batteryLevelDescriptor->setUnit(0x27ad);
|
|
|
|
batteryService->start();
|
|
}
|
|
|
|
void NimbleBluetooth::startAdvertising()
|
|
{
|
|
NimBLEAdvertising *pAdvertising = NimBLEDevice::getAdvertising();
|
|
pAdvertising->reset();
|
|
pAdvertising->addServiceUUID(MESH_SERVICE_UUID);
|
|
pAdvertising->addServiceUUID(NimBLEUUID((uint16_t)0x180f)); // 0x180F is the Battery Service
|
|
pAdvertising->start(0);
|
|
}
|
|
|
|
/// Given a level between 0-100, update the BLE attribute
|
|
void updateBatteryLevel(uint8_t level)
|
|
{
|
|
if ((config.bluetooth.enabled == true) && bleServer && nimbleBluetooth->isConnected()) {
|
|
BatteryCharacteristic->setValue(&level, 1);
|
|
BatteryCharacteristic->notify();
|
|
}
|
|
}
|
|
|
|
void NimbleBluetooth::clearBonds()
|
|
{
|
|
LOG_INFO("Clearing bluetooth bonds!\n");
|
|
NimBLEDevice::deleteAllBonds();
|
|
}
|
|
|
|
void NimbleBluetooth::sendLog(const char *logMessage)
|
|
{
|
|
if (!bleServer || !isConnected() || strlen(logMessage) > 512) {
|
|
return;
|
|
}
|
|
logRadioCharacteristic->notify(reinterpret_cast<const uint8_t *>(logMessage), strlen(logMessage), true);
|
|
}
|
|
|
|
void clearNVS()
|
|
{
|
|
NimBLEDevice::deleteAllBonds();
|
|
#ifdef ARCH_ESP32
|
|
ESP.restart();
|
|
#endif
|
|
}
|
|
#endif |