mirror of
https://github.com/meshtastic/firmware.git
synced 2026-01-25 03:07:44 +00:00
code format
This commit is contained in:
@@ -10,21 +10,24 @@
|
||||
#include <utility/bonding.h>
|
||||
static BLEService meshBleService = BLEService(BLEUuid(MESH_SERVICE_UUID_16));
|
||||
static BLECharacteristic fromNum = BLECharacteristic(BLEUuid(FROMNUM_UUID_16));
|
||||
static BLECharacteristic fromRadio = BLECharacteristic(BLEUuid(FROMRADIO_UUID_16));
|
||||
static BLECharacteristic fromRadio =
|
||||
BLECharacteristic(BLEUuid(FROMRADIO_UUID_16));
|
||||
static BLECharacteristic toRadio = BLECharacteristic(BLEUuid(TORADIO_UUID_16));
|
||||
static BLECharacteristic logRadio = BLECharacteristic(BLEUuid(LOGRADIO_UUID_16));
|
||||
static BLECharacteristic logRadio =
|
||||
BLECharacteristic(BLEUuid(LOGRADIO_UUID_16));
|
||||
|
||||
static BLEDis bledis; // DIS (Device Information Service) helper class instance
|
||||
static BLEBas blebas; // BAS (Battery Service) helper class instance
|
||||
#ifndef BLE_DFU_SECURE
|
||||
static BLEDfu bledfu; // DFU software update helper service
|
||||
#else
|
||||
static BLEDfuSecure bledfusecure; // DFU software update helper service
|
||||
static BLEDfuSecure bledfusecure; // DFU software update helper service
|
||||
#endif
|
||||
|
||||
// This scratch buffer is used for various bluetooth reads/writes - but it is safe because only one bt operation can be in
|
||||
// process at once
|
||||
// static uint8_t trBytes[_max(_max(_max(_max(ToRadio_size, RadioConfig_size), User_size), MyNodeInfo_size), FromRadio_size)];
|
||||
// This scratch buffer is used for various bluetooth reads/writes - but it is
|
||||
// safe because only one bt operation can be in process at once static uint8_t
|
||||
// trBytes[_max(_max(_max(_max(ToRadio_size, RadioConfig_size), User_size),
|
||||
// MyNodeInfo_size), FromRadio_size)];
|
||||
static uint8_t fromRadioBytes[meshtastic_FromRadio_size];
|
||||
static uint8_t toRadioBytes[meshtastic_ToRadio_size];
|
||||
|
||||
@@ -33,427 +36,451 @@ static uint8_t lastToRadio[MAX_TO_FROM_RADIO_SIZE];
|
||||
|
||||
static uint16_t connectionHandle;
|
||||
|
||||
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) override
|
||||
{
|
||||
PhoneAPI::onNowHasData(fromRadioNum);
|
||||
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) override {
|
||||
PhoneAPI::onNowHasData(fromRadioNum);
|
||||
|
||||
LOG_INFO("BLE notify fromNum");
|
||||
fromNum.notify32(fromRadioNum);
|
||||
}
|
||||
LOG_INFO("BLE notify fromNum");
|
||||
fromNum.notify32(fromRadioNum);
|
||||
}
|
||||
|
||||
/// Check the current underlying physical link to see if the client is currently connected
|
||||
virtual bool checkIsConnected() override { return Bluefruit.connected(connectionHandle); }
|
||||
/// Check the current underlying physical link to see if the client is
|
||||
/// currently connected
|
||||
virtual bool checkIsConnected() override {
|
||||
return Bluefruit.connected(connectionHandle);
|
||||
}
|
||||
|
||||
public:
|
||||
BluetoothPhoneAPI() { api_type = TYPE_BLE; }
|
||||
public:
|
||||
BluetoothPhoneAPI() { api_type = TYPE_BLE; }
|
||||
};
|
||||
|
||||
static BluetoothPhoneAPI *bluetoothPhoneAPI;
|
||||
|
||||
void onConnect(uint16_t conn_handle)
|
||||
{
|
||||
// Get the reference to current connection
|
||||
BLEConnection *connection = Bluefruit.Connection(conn_handle);
|
||||
connectionHandle = conn_handle;
|
||||
char central_name[32] = {0};
|
||||
connection->getPeerName(central_name, sizeof(central_name));
|
||||
LOG_INFO("BLE Connected to %s", central_name);
|
||||
void onConnect(uint16_t conn_handle) {
|
||||
// Get the reference to current connection
|
||||
BLEConnection *connection = Bluefruit.Connection(conn_handle);
|
||||
connectionHandle = conn_handle;
|
||||
char central_name[32] = {0};
|
||||
connection->getPeerName(central_name, sizeof(central_name));
|
||||
LOG_INFO("BLE Connected to %s", central_name);
|
||||
|
||||
// Notify UI (or any other interested firmware components)
|
||||
meshtastic::BluetoothStatus newStatus(meshtastic::BluetoothStatus::ConnectionState::CONNECTED);
|
||||
bluetoothStatus->updateStatus(&newStatus);
|
||||
// Notify UI (or any other interested firmware components)
|
||||
meshtastic::BluetoothStatus newStatus(
|
||||
meshtastic::BluetoothStatus::ConnectionState::CONNECTED);
|
||||
bluetoothStatus->updateStatus(&newStatus);
|
||||
}
|
||||
/**
|
||||
* Callback invoked when a connection is dropped
|
||||
* @param conn_handle connection where this event happens
|
||||
* @param reason is a BLE_HCI_STATUS_CODE which can be found in ble_hci.h
|
||||
*/
|
||||
void onDisconnect(uint16_t conn_handle, uint8_t reason)
|
||||
{
|
||||
LOG_INFO("BLE Disconnected, reason = 0x%x", reason);
|
||||
if (bluetoothPhoneAPI) {
|
||||
bluetoothPhoneAPI->close();
|
||||
}
|
||||
void onDisconnect(uint16_t conn_handle, uint8_t reason) {
|
||||
LOG_INFO("BLE Disconnected, reason = 0x%x", reason);
|
||||
if (bluetoothPhoneAPI) {
|
||||
bluetoothPhoneAPI->close();
|
||||
}
|
||||
|
||||
// Clear the last ToRadio packet buffer to avoid rejecting first packet from new connection
|
||||
memset(lastToRadio, 0, sizeof(lastToRadio));
|
||||
// Clear the last ToRadio packet buffer to avoid rejecting first packet from
|
||||
// new connection
|
||||
memset(lastToRadio, 0, sizeof(lastToRadio));
|
||||
|
||||
// Notify UI (or any other interested firmware components)
|
||||
meshtastic::BluetoothStatus newStatus(meshtastic::BluetoothStatus::ConnectionState::DISCONNECTED);
|
||||
bluetoothStatus->updateStatus(&newStatus);
|
||||
// Notify UI (or any other interested firmware components)
|
||||
meshtastic::BluetoothStatus newStatus(
|
||||
meshtastic::BluetoothStatus::ConnectionState::DISCONNECTED);
|
||||
bluetoothStatus->updateStatus(&newStatus);
|
||||
}
|
||||
void onCccd(uint16_t conn_hdl, BLECharacteristic *chr, uint16_t cccd_value)
|
||||
{
|
||||
// Display the raw request packet
|
||||
LOG_INFO("CCCD Updated: %u", cccd_value);
|
||||
// Check the characteristic this CCCD update is associated with in case
|
||||
// this handler is used for multiple CCCD records.
|
||||
void onCccd(uint16_t conn_hdl, BLECharacteristic *chr, uint16_t cccd_value) {
|
||||
// Display the raw request packet
|
||||
LOG_INFO("CCCD Updated: %u", cccd_value);
|
||||
// Check the characteristic this CCCD update is associated with in case
|
||||
// this handler is used for multiple CCCD records.
|
||||
|
||||
// According to the GATT spec: cccd value = 0x0001 means notifications are enabled
|
||||
// and cccd value = 0x0002 means indications are enabled
|
||||
// According to the GATT spec: cccd value = 0x0001 means notifications are
|
||||
// enabled and cccd value = 0x0002 means indications are enabled
|
||||
|
||||
if (chr->uuid == fromNum.uuid || chr->uuid == logRadio.uuid) {
|
||||
auto result = cccd_value == 2 ? chr->indicateEnabled(conn_hdl) : chr->notifyEnabled(conn_hdl);
|
||||
if (result) {
|
||||
LOG_INFO("Notify/Indicate enabled");
|
||||
} else {
|
||||
LOG_INFO("Notify/Indicate disabled");
|
||||
}
|
||||
if (chr->uuid == fromNum.uuid || chr->uuid == logRadio.uuid) {
|
||||
auto result = cccd_value == 2 ? chr->indicateEnabled(conn_hdl)
|
||||
: chr->notifyEnabled(conn_hdl);
|
||||
if (result) {
|
||||
LOG_INFO("Notify/Indicate enabled");
|
||||
} else {
|
||||
LOG_INFO("Notify/Indicate disabled");
|
||||
}
|
||||
}
|
||||
}
|
||||
void startAdv(void)
|
||||
{
|
||||
// Advertising packet
|
||||
Bluefruit.Advertising.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE);
|
||||
// IncludeService UUID
|
||||
// Bluefruit.ScanResponse.addService(meshBleService);
|
||||
Bluefruit.ScanResponse.addTxPower();
|
||||
Bluefruit.ScanResponse.addName();
|
||||
// Include Name
|
||||
// Bluefruit.Advertising.addName();
|
||||
Bluefruit.Advertising.addService(meshBleService);
|
||||
/* Start Advertising
|
||||
* - Enable auto advertising if disconnected
|
||||
* - Interval: fast mode = 20 ms, slow mode = 417,5 ms
|
||||
* - Timeout for fast mode is 30 seconds
|
||||
* - Start(timeout) with timeout = 0 will advertise forever (until connected)
|
||||
*
|
||||
* For recommended advertising interval
|
||||
* https://developer.apple.com/library/content/qa/qa1931/_index.html
|
||||
*/
|
||||
Bluefruit.Advertising.restartOnDisconnect(true);
|
||||
Bluefruit.Advertising.setInterval(32, 668); // in unit of 0.625 ms
|
||||
Bluefruit.Advertising.setFastTimeout(30); // number of seconds in fast mode
|
||||
Bluefruit.Advertising.start(0); // 0 = Don't stop advertising after n seconds. FIXME, we should stop advertising after X
|
||||
void startAdv(void) {
|
||||
// Advertising packet
|
||||
Bluefruit.Advertising.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE);
|
||||
// IncludeService UUID
|
||||
// Bluefruit.ScanResponse.addService(meshBleService);
|
||||
Bluefruit.ScanResponse.addTxPower();
|
||||
Bluefruit.ScanResponse.addName();
|
||||
// Include Name
|
||||
// Bluefruit.Advertising.addName();
|
||||
Bluefruit.Advertising.addService(meshBleService);
|
||||
/* Start Advertising
|
||||
* - Enable auto advertising if disconnected
|
||||
* - Interval: fast mode = 20 ms, slow mode = 417,5 ms
|
||||
* - Timeout for fast mode is 30 seconds
|
||||
* - Start(timeout) with timeout = 0 will advertise forever (until connected)
|
||||
*
|
||||
* For recommended advertising interval
|
||||
* https://developer.apple.com/library/content/qa/qa1931/_index.html
|
||||
*/
|
||||
Bluefruit.Advertising.restartOnDisconnect(true);
|
||||
Bluefruit.Advertising.setInterval(32, 668); // in unit of 0.625 ms
|
||||
Bluefruit.Advertising.setFastTimeout(30); // number of seconds in fast mode
|
||||
Bluefruit.Advertising.start(0); // 0 = Don't stop advertising after n seconds.
|
||||
// FIXME, we should stop advertising after X
|
||||
}
|
||||
// Just ack that the caller is allowed to read
|
||||
static void authorizeRead(uint16_t conn_hdl)
|
||||
{
|
||||
ble_gatts_rw_authorize_reply_params_t reply = {.type = BLE_GATTS_AUTHORIZE_TYPE_READ};
|
||||
reply.params.write.gatt_status = BLE_GATT_STATUS_SUCCESS;
|
||||
sd_ble_gatts_rw_authorize_reply(conn_hdl, &reply);
|
||||
static void authorizeRead(uint16_t conn_hdl) {
|
||||
ble_gatts_rw_authorize_reply_params_t reply = {
|
||||
.type = BLE_GATTS_AUTHORIZE_TYPE_READ};
|
||||
reply.params.write.gatt_status = BLE_GATT_STATUS_SUCCESS;
|
||||
sd_ble_gatts_rw_authorize_reply(conn_hdl, &reply);
|
||||
}
|
||||
/**
|
||||
* client is starting read, pull the bytes from our API class
|
||||
*/
|
||||
void onFromRadioAuthorize(uint16_t conn_hdl, BLECharacteristic *chr, ble_gatts_evt_read_t *request)
|
||||
{
|
||||
if (request->offset == 0) {
|
||||
// If the read is long, we will get multiple authorize invocations - we only populate data on the first
|
||||
size_t numBytes = bluetoothPhoneAPI->getFromRadio(fromRadioBytes);
|
||||
// Someone is going to read our value as soon as this callback returns. So fill it with the next message in the queue
|
||||
// or make empty if the queue is empty
|
||||
fromRadio.write(fromRadioBytes, numBytes);
|
||||
} else {
|
||||
// LOG_INFO("Ignore successor read");
|
||||
}
|
||||
authorizeRead(conn_hdl);
|
||||
void onFromRadioAuthorize(uint16_t conn_hdl, BLECharacteristic *chr,
|
||||
ble_gatts_evt_read_t *request) {
|
||||
if (request->offset == 0) {
|
||||
// If the read is long, we will get multiple authorize invocations - we only
|
||||
// populate data on the first
|
||||
size_t numBytes = bluetoothPhoneAPI->getFromRadio(fromRadioBytes);
|
||||
// Someone is going to read our value as soon as this callback returns. So
|
||||
// fill it with the next message in the queue or make empty if the queue is
|
||||
// empty
|
||||
fromRadio.write(fromRadioBytes, numBytes);
|
||||
} else {
|
||||
// LOG_INFO("Ignore successor read");
|
||||
}
|
||||
authorizeRead(conn_hdl);
|
||||
}
|
||||
|
||||
void onToRadioWrite(uint16_t conn_hdl, BLECharacteristic *chr, uint8_t *data, uint16_t len)
|
||||
{
|
||||
LOG_INFO("toRadioWriteCb data %p, len %u", data, len);
|
||||
if (memcmp(lastToRadio, data, len) != 0) {
|
||||
LOG_DEBUG("New ToRadio packet");
|
||||
memcpy(lastToRadio, data, len);
|
||||
bluetoothPhoneAPI->handleToRadio(data, len);
|
||||
} else {
|
||||
LOG_DEBUG("Drop dup ToRadio packet we just saw");
|
||||
}
|
||||
void onToRadioWrite(uint16_t conn_hdl, BLECharacteristic *chr, uint8_t *data,
|
||||
uint16_t len) {
|
||||
LOG_INFO("toRadioWriteCb data %p, len %u", data, len);
|
||||
if (memcmp(lastToRadio, data, len) != 0) {
|
||||
LOG_DEBUG("New ToRadio packet");
|
||||
memcpy(lastToRadio, data, len);
|
||||
bluetoothPhoneAPI->handleToRadio(data, len);
|
||||
} else {
|
||||
LOG_DEBUG("Drop dup ToRadio packet we just saw");
|
||||
}
|
||||
}
|
||||
|
||||
void setupMeshService(void)
|
||||
{
|
||||
bluetoothPhoneAPI = new BluetoothPhoneAPI();
|
||||
meshBleService.begin();
|
||||
// Note: You must call .begin() on the BLEService before calling .begin() on
|
||||
// any characteristic(s) within that service definition.. Calling .begin() on
|
||||
// a BLECharacteristic will cause it to be added to the last BLEService that
|
||||
// was 'begin()'ed!
|
||||
auto secMode =
|
||||
config.bluetooth.mode == meshtastic_Config_BluetoothConfig_PairingMode_NO_PIN ? SECMODE_OPEN : SECMODE_ENC_NO_MITM;
|
||||
fromNum.setProperties(CHR_PROPS_NOTIFY | CHR_PROPS_READ);
|
||||
fromNum.setPermission(secMode, SECMODE_NO_ACCESS); // FIXME, secure this!!!
|
||||
fromNum.setFixedLen(
|
||||
0); // Variable len (either 0 or 4) FIXME consider changing protocol so it is fixed 4 byte len, where 0 means empty
|
||||
fromNum.setMaxLen(4);
|
||||
fromNum.setCccdWriteCallback(onCccd); // Optionally capture CCCD updates
|
||||
// We don't yet need to hook the fromNum auth callback
|
||||
// fromNum.setReadAuthorizeCallback(fromNumAuthorizeCb);
|
||||
fromNum.write32(0); // Provide default fromNum of 0
|
||||
fromNum.begin();
|
||||
void setupMeshService(void) {
|
||||
bluetoothPhoneAPI = new BluetoothPhoneAPI();
|
||||
meshBleService.begin();
|
||||
// Note: You must call .begin() on the BLEService before calling .begin() on
|
||||
// any characteristic(s) within that service definition.. Calling .begin() on
|
||||
// a BLECharacteristic will cause it to be added to the last BLEService that
|
||||
// was 'begin()'ed!
|
||||
auto secMode = config.bluetooth.mode ==
|
||||
meshtastic_Config_BluetoothConfig_PairingMode_NO_PIN
|
||||
? SECMODE_OPEN
|
||||
: SECMODE_ENC_NO_MITM;
|
||||
fromNum.setProperties(CHR_PROPS_NOTIFY | CHR_PROPS_READ);
|
||||
fromNum.setPermission(secMode, SECMODE_NO_ACCESS); // FIXME, secure this!!!
|
||||
fromNum.setFixedLen(
|
||||
0); // Variable len (either 0 or 4) FIXME consider changing protocol so
|
||||
// it is fixed 4 byte len, where 0 means empty
|
||||
fromNum.setMaxLen(4);
|
||||
fromNum.setCccdWriteCallback(onCccd); // Optionally capture CCCD updates
|
||||
// We don't yet need to hook the fromNum auth callback
|
||||
// fromNum.setReadAuthorizeCallback(fromNumAuthorizeCb);
|
||||
fromNum.write32(0); // Provide default fromNum of 0
|
||||
fromNum.begin();
|
||||
|
||||
fromRadio.setProperties(CHR_PROPS_READ);
|
||||
fromRadio.setPermission(secMode, SECMODE_NO_ACCESS);
|
||||
fromRadio.setMaxLen(sizeof(fromRadioBytes));
|
||||
fromRadio.setReadAuthorizeCallback(
|
||||
onFromRadioAuthorize,
|
||||
false); // We don't call this callback via the adafruit queue, because we can safely run in the BLE context
|
||||
fromRadio.setBuffer(fromRadioBytes, sizeof(fromRadioBytes)); // we preallocate our fromradio buffer so we won't waste space
|
||||
// for two copies
|
||||
fromRadio.begin();
|
||||
fromRadio.setProperties(CHR_PROPS_READ);
|
||||
fromRadio.setPermission(secMode, SECMODE_NO_ACCESS);
|
||||
fromRadio.setMaxLen(sizeof(fromRadioBytes));
|
||||
fromRadio.setReadAuthorizeCallback(
|
||||
onFromRadioAuthorize,
|
||||
false); // We don't call this callback via the adafruit queue, because we
|
||||
// can safely run in the BLE context
|
||||
fromRadio.setBuffer(fromRadioBytes,
|
||||
sizeof(fromRadioBytes)); // we preallocate our fromradio
|
||||
// buffer so we won't waste space
|
||||
// for two copies
|
||||
fromRadio.begin();
|
||||
|
||||
toRadio.setProperties(CHR_PROPS_WRITE);
|
||||
toRadio.setPermission(secMode, secMode); // FIXME secure this!
|
||||
toRadio.setFixedLen(0);
|
||||
toRadio.setMaxLen(512);
|
||||
toRadio.setBuffer(toRadioBytes, sizeof(toRadioBytes));
|
||||
// We don't call this callback via the adafruit queue, because we can safely run in the BLE context
|
||||
toRadio.setWriteCallback(onToRadioWrite, false);
|
||||
toRadio.begin();
|
||||
toRadio.setProperties(CHR_PROPS_WRITE);
|
||||
toRadio.setPermission(secMode, secMode); // FIXME secure this!
|
||||
toRadio.setFixedLen(0);
|
||||
toRadio.setMaxLen(512);
|
||||
toRadio.setBuffer(toRadioBytes, sizeof(toRadioBytes));
|
||||
// We don't call this callback via the adafruit queue, because we can safely
|
||||
// run in the BLE context
|
||||
toRadio.setWriteCallback(onToRadioWrite, false);
|
||||
toRadio.begin();
|
||||
|
||||
logRadio.setProperties(CHR_PROPS_INDICATE | CHR_PROPS_NOTIFY | CHR_PROPS_READ);
|
||||
logRadio.setPermission(secMode, SECMODE_NO_ACCESS);
|
||||
logRadio.setMaxLen(512);
|
||||
logRadio.setCccdWriteCallback(onCccd);
|
||||
logRadio.write32(0);
|
||||
logRadio.begin();
|
||||
logRadio.setProperties(CHR_PROPS_INDICATE | CHR_PROPS_NOTIFY |
|
||||
CHR_PROPS_READ);
|
||||
logRadio.setPermission(secMode, SECMODE_NO_ACCESS);
|
||||
logRadio.setMaxLen(512);
|
||||
logRadio.setCccdWriteCallback(onCccd);
|
||||
logRadio.write32(0);
|
||||
logRadio.begin();
|
||||
}
|
||||
static uint32_t configuredPasskey;
|
||||
void NRF52Bluetooth::shutdown()
|
||||
{
|
||||
// Shutdown bluetooth for minimum power draw
|
||||
LOG_INFO("Disable NRF52 bluetooth");
|
||||
Bluefruit.Security.setPairPasskeyCallback(NRF52Bluetooth::onUnwantedPairing); // Actively refuse (during factory reset)
|
||||
disconnect();
|
||||
Bluefruit.Advertising.stop();
|
||||
void NRF52Bluetooth::shutdown() {
|
||||
// Shutdown bluetooth for minimum power draw
|
||||
LOG_INFO("Disable NRF52 bluetooth");
|
||||
Bluefruit.Security.setPairPasskeyCallback(
|
||||
NRF52Bluetooth::onUnwantedPairing); // Actively refuse (during factory
|
||||
// reset)
|
||||
disconnect();
|
||||
Bluefruit.Advertising.stop();
|
||||
}
|
||||
void NRF52Bluetooth::startDisabled()
|
||||
{
|
||||
// Setup Bluetooth
|
||||
nrf52Bluetooth->setup();
|
||||
// Shutdown bluetooth for minimum power draw
|
||||
Bluefruit.Advertising.stop();
|
||||
Bluefruit.setTxPower(-40); // Minimum power
|
||||
LOG_INFO("Disable NRF52 Bluetooth. (Workaround: tx power min, advertise stopped)");
|
||||
void NRF52Bluetooth::startDisabled() {
|
||||
// Setup Bluetooth
|
||||
nrf52Bluetooth->setup();
|
||||
// Shutdown bluetooth for minimum power draw
|
||||
Bluefruit.Advertising.stop();
|
||||
Bluefruit.setTxPower(-40); // Minimum power
|
||||
LOG_INFO(
|
||||
"Disable NRF52 Bluetooth. (Workaround: tx power min, advertise stopped)");
|
||||
}
|
||||
bool NRF52Bluetooth::isConnected()
|
||||
{
|
||||
return Bluefruit.connected(connectionHandle);
|
||||
bool NRF52Bluetooth::isConnected() {
|
||||
return Bluefruit.connected(connectionHandle);
|
||||
}
|
||||
int NRF52Bluetooth::getRssi()
|
||||
{
|
||||
return 0; // FIXME figure out where to source this
|
||||
int NRF52Bluetooth::getRssi() {
|
||||
return 0; // FIXME figure out where to source this
|
||||
}
|
||||
void NRF52Bluetooth::setup()
|
||||
{
|
||||
// Initialise the Bluefruit module
|
||||
LOG_INFO("Init the Bluefruit nRF52 module");
|
||||
Bluefruit.autoConnLed(false);
|
||||
Bluefruit.configPrphBandwidth(BANDWIDTH_MAX);
|
||||
Bluefruit.begin();
|
||||
// Clear existing data.
|
||||
Bluefruit.Advertising.stop();
|
||||
Bluefruit.Advertising.clearData();
|
||||
Bluefruit.ScanResponse.clearData();
|
||||
if (config.bluetooth.mode != meshtastic_Config_BluetoothConfig_PairingMode_NO_PIN) {
|
||||
configuredPasskey = config.bluetooth.mode == meshtastic_Config_BluetoothConfig_PairingMode_FIXED_PIN
|
||||
? config.bluetooth.fixed_pin
|
||||
: random(100000, 999999);
|
||||
auto pinString = std::to_string(configuredPasskey);
|
||||
LOG_INFO("Bluetooth pin set to '%i'", configuredPasskey);
|
||||
Bluefruit.Security.setPIN(pinString.c_str());
|
||||
Bluefruit.Security.setIOCaps(true, false, false);
|
||||
Bluefruit.Security.setPairPasskeyCallback(NRF52Bluetooth::onPairingPasskey);
|
||||
Bluefruit.Security.setPairCompleteCallback(NRF52Bluetooth::onPairingCompleted);
|
||||
Bluefruit.Security.setSecuredCallback(NRF52Bluetooth::onConnectionSecured);
|
||||
meshBleService.setPermission(SECMODE_ENC_WITH_MITM, SECMODE_ENC_WITH_MITM);
|
||||
} else {
|
||||
Bluefruit.Security.setIOCaps(false, false, false);
|
||||
meshBleService.setPermission(SECMODE_OPEN, SECMODE_OPEN);
|
||||
}
|
||||
// Set the advertised device name (keep it short!)
|
||||
Bluefruit.setName(getDeviceName());
|
||||
// Set the connect/disconnect callback handlers
|
||||
Bluefruit.Periph.setConnectCallback(onConnect);
|
||||
Bluefruit.Periph.setDisconnectCallback(onDisconnect);
|
||||
void NRF52Bluetooth::setup() {
|
||||
// Initialise the Bluefruit module
|
||||
LOG_INFO("Init the Bluefruit nRF52 module");
|
||||
Bluefruit.autoConnLed(false);
|
||||
Bluefruit.configPrphBandwidth(BANDWIDTH_MAX);
|
||||
Bluefruit.begin();
|
||||
// Clear existing data.
|
||||
Bluefruit.Advertising.stop();
|
||||
Bluefruit.Advertising.clearData();
|
||||
Bluefruit.ScanResponse.clearData();
|
||||
if (config.bluetooth.mode !=
|
||||
meshtastic_Config_BluetoothConfig_PairingMode_NO_PIN) {
|
||||
configuredPasskey =
|
||||
config.bluetooth.mode ==
|
||||
meshtastic_Config_BluetoothConfig_PairingMode_FIXED_PIN
|
||||
? config.bluetooth.fixed_pin
|
||||
: random(100000, 999999);
|
||||
auto pinString = std::to_string(configuredPasskey);
|
||||
LOG_INFO("Bluetooth pin set to '%i'", configuredPasskey);
|
||||
Bluefruit.Security.setPIN(pinString.c_str());
|
||||
Bluefruit.Security.setIOCaps(true, false, false);
|
||||
Bluefruit.Security.setPairPasskeyCallback(NRF52Bluetooth::onPairingPasskey);
|
||||
Bluefruit.Security.setPairCompleteCallback(
|
||||
NRF52Bluetooth::onPairingCompleted);
|
||||
Bluefruit.Security.setSecuredCallback(NRF52Bluetooth::onConnectionSecured);
|
||||
meshBleService.setPermission(SECMODE_ENC_WITH_MITM, SECMODE_ENC_WITH_MITM);
|
||||
} else {
|
||||
Bluefruit.Security.setIOCaps(false, false, false);
|
||||
meshBleService.setPermission(SECMODE_OPEN, SECMODE_OPEN);
|
||||
}
|
||||
// Set the advertised device name (keep it short!)
|
||||
Bluefruit.setName(getDeviceName());
|
||||
// Set the connect/disconnect callback handlers
|
||||
Bluefruit.Periph.setConnectCallback(onConnect);
|
||||
Bluefruit.Periph.setDisconnectCallback(onDisconnect);
|
||||
|
||||
|
||||
// Do not change Slave Latency to value other than 0 !!!
|
||||
// There is probably a bug in SoftDevice + certain Apple iOS versions being
|
||||
// brain damaged causing connectivity problems.
|
||||
// Do not change Slave Latency to value other than 0 !!!
|
||||
// There is probably a bug in SoftDevice + certain Apple iOS versions being
|
||||
// brain damaged causing connectivity problems.
|
||||
|
||||
// On one side it seems SoftDevice is using SlaveLatency value even
|
||||
// if connection parameter negotation failed and phone sees it as connectivity errors.
|
||||
// On one side it seems SoftDevice is using SlaveLatency value even
|
||||
// if connection parameter negotation failed and phone sees it as connectivity
|
||||
// errors.
|
||||
|
||||
// On the other hand Apple can randomly refuse any parameter negotiation and shutdown connection
|
||||
// even if you meet Apple Developer Guidelines for BLE devices. Because f* you, that's why.
|
||||
// On the other hand Apple can randomly refuse any parameter negotiation and
|
||||
// shutdown connection even if you meet Apple Developer Guidelines for BLE
|
||||
// devices. Because f* you, that's why.
|
||||
|
||||
// While this API call sets preferred connection parameters (PPCP) - many phones ignore it (yeah) and it seems SoftDevice
|
||||
// will try to renegotiate connection parameters based on those values after phone connection.
|
||||
// So those are relatively safe values so Apple braindead firmware won't get angry and at least we may try
|
||||
// to negotiate some longer connection interval to save battery.
|
||||
// While this API call sets preferred connection parameters (PPCP) - many
|
||||
// phones ignore it (yeah) and it seems SoftDevice will try to renegotiate
|
||||
// connection parameters based on those values after phone connection. So
|
||||
// those are relatively safe values so Apple braindead firmware won't get
|
||||
// angry and at least we may try to negotiate some longer connection interval
|
||||
// to save battery.
|
||||
|
||||
// See https://github.com/meshtastic/firmware/pull/8858 for measurements. We are dealing with microamp savings anyway so not worth
|
||||
// dying on a hill here.
|
||||
// See https://github.com/meshtastic/firmware/pull/8858 for measurements. We
|
||||
// are dealing with microamp savings anyway so not worth dying on a hill here.
|
||||
|
||||
Bluefruit.Periph.setConnSlaveLatency(0);
|
||||
// 1.25 ms units - so min, max is 15, 100 ms range.
|
||||
Bluefruit.Periph.setConnInterval(12, 80);
|
||||
Bluefruit.Periph.setConnSlaveLatency(0);
|
||||
// 1.25 ms units - so min, max is 15, 100 ms range.
|
||||
Bluefruit.Periph.setConnInterval(12, 80);
|
||||
|
||||
#ifndef BLE_DFU_SECURE
|
||||
bledfu.setPermission(SECMODE_ENC_WITH_MITM, SECMODE_ENC_WITH_MITM);
|
||||
bledfu.begin(); // Install the DFU helper
|
||||
bledfu.setPermission(SECMODE_ENC_WITH_MITM, SECMODE_ENC_WITH_MITM);
|
||||
bledfu.begin(); // Install the DFU helper
|
||||
#else
|
||||
bledfusecure.setPermission(SECMODE_ENC_WITH_MITM, SECMODE_ENC_WITH_MITM); // add by WayenWeng
|
||||
bledfusecure.begin(); // Install the DFU helper
|
||||
bledfusecure.setPermission(SECMODE_ENC_WITH_MITM,
|
||||
SECMODE_ENC_WITH_MITM); // add by WayenWeng
|
||||
bledfusecure.begin(); // Install the DFU helper
|
||||
#endif
|
||||
// Configure and Start the Device Information Service
|
||||
LOG_INFO("Init the Device Information Service");
|
||||
bledis.setModel(optstr(HW_VERSION));
|
||||
bledis.setFirmwareRev(optstr(APP_VERSION));
|
||||
bledis.begin();
|
||||
// Start the BLE Battery Service and set it to 100%
|
||||
LOG_INFO("Init the Battery Service");
|
||||
blebas.begin();
|
||||
blebas.write(0); // Unknown battery level for now
|
||||
// Setup the Heart Rate Monitor service using
|
||||
// BLEService and BLECharacteristic classes
|
||||
LOG_INFO("Init the Mesh bluetooth service");
|
||||
setupMeshService();
|
||||
// Setup the advertising packet(s)
|
||||
LOG_INFO("Set up the advertising payload(s)");
|
||||
startAdv();
|
||||
LOG_INFO("Advertise");
|
||||
// Configure and Start the Device Information Service
|
||||
LOG_INFO("Init the Device Information Service");
|
||||
bledis.setModel(optstr(HW_VERSION));
|
||||
bledis.setFirmwareRev(optstr(APP_VERSION));
|
||||
bledis.begin();
|
||||
// Start the BLE Battery Service and set it to 100%
|
||||
LOG_INFO("Init the Battery Service");
|
||||
blebas.begin();
|
||||
blebas.write(0); // Unknown battery level for now
|
||||
// Setup the Heart Rate Monitor service using
|
||||
// BLEService and BLECharacteristic classes
|
||||
LOG_INFO("Init the Mesh bluetooth service");
|
||||
setupMeshService();
|
||||
// Setup the advertising packet(s)
|
||||
LOG_INFO("Set up the advertising payload(s)");
|
||||
startAdv();
|
||||
LOG_INFO("Advertise");
|
||||
}
|
||||
void NRF52Bluetooth::resumeAdvertising()
|
||||
{
|
||||
Bluefruit.Advertising.restartOnDisconnect(true);
|
||||
Bluefruit.Advertising.setInterval(32, 668); // in unit of 0.625 ms
|
||||
Bluefruit.Advertising.setFastTimeout(30); // number of seconds in fast mode
|
||||
Bluefruit.Advertising.start(0);
|
||||
void NRF52Bluetooth::resumeAdvertising() {
|
||||
Bluefruit.Advertising.restartOnDisconnect(true);
|
||||
Bluefruit.Advertising.setInterval(32, 668); // in unit of 0.625 ms
|
||||
Bluefruit.Advertising.setFastTimeout(30); // number of seconds in fast mode
|
||||
Bluefruit.Advertising.start(0);
|
||||
}
|
||||
/// Given a level between 0-100, update the BLE attribute
|
||||
void updateBatteryLevel(uint8_t level)
|
||||
{
|
||||
blebas.write(level);
|
||||
void updateBatteryLevel(uint8_t level) { blebas.write(level); }
|
||||
void NRF52Bluetooth::clearBonds() {
|
||||
LOG_INFO("Clear bluetooth bonds!");
|
||||
bond_print_list(BLE_GAP_ROLE_PERIPH);
|
||||
bond_print_list(BLE_GAP_ROLE_CENTRAL);
|
||||
Bluefruit.Periph.clearBonds();
|
||||
Bluefruit.Central.clearBonds();
|
||||
}
|
||||
void NRF52Bluetooth::clearBonds()
|
||||
{
|
||||
LOG_INFO("Clear bluetooth bonds!");
|
||||
bond_print_list(BLE_GAP_ROLE_PERIPH);
|
||||
bond_print_list(BLE_GAP_ROLE_CENTRAL);
|
||||
Bluefruit.Periph.clearBonds();
|
||||
Bluefruit.Central.clearBonds();
|
||||
void NRF52Bluetooth::onConnectionSecured(uint16_t conn_handle) {
|
||||
LOG_INFO("BLE connection secured");
|
||||
}
|
||||
void NRF52Bluetooth::onConnectionSecured(uint16_t conn_handle)
|
||||
{
|
||||
LOG_INFO("BLE connection secured");
|
||||
}
|
||||
bool NRF52Bluetooth::onPairingPasskey(uint16_t conn_handle, uint8_t const passkey[6], bool match_request)
|
||||
{
|
||||
char passkey1[4] = {passkey[0], passkey[1], passkey[2], '\0'};
|
||||
char passkey2[4] = {passkey[3], passkey[4], passkey[5], '\0'};
|
||||
LOG_INFO("BLE pair process started with passkey %s %s", passkey1, passkey2);
|
||||
powerFSM.trigger(EVENT_BLUETOOTH_PAIR);
|
||||
bool NRF52Bluetooth::onPairingPasskey(uint16_t conn_handle,
|
||||
uint8_t const passkey[6],
|
||||
bool match_request) {
|
||||
char passkey1[4] = {passkey[0], passkey[1], passkey[2], '\0'};
|
||||
char passkey2[4] = {passkey[3], passkey[4], passkey[5], '\0'};
|
||||
LOG_INFO("BLE pair process started with passkey %s %s", passkey1, passkey2);
|
||||
powerFSM.trigger(EVENT_BLUETOOTH_PAIR);
|
||||
|
||||
// Get passkey as string
|
||||
// Note: possible leading zeros
|
||||
std::string textkey;
|
||||
for (uint8_t i = 0; i < 6; i++)
|
||||
textkey += (char)passkey[i];
|
||||
// Get passkey as string
|
||||
// Note: possible leading zeros
|
||||
std::string textkey;
|
||||
for (uint8_t i = 0; i < 6; i++)
|
||||
textkey += (char)passkey[i];
|
||||
|
||||
// Notify UI (or other components) of pairing event and passkey
|
||||
meshtastic::BluetoothStatus newStatus(textkey);
|
||||
bluetoothStatus->updateStatus(&newStatus);
|
||||
// Notify UI (or other components) of pairing event and passkey
|
||||
meshtastic::BluetoothStatus newStatus(textkey);
|
||||
bluetoothStatus->updateStatus(&newStatus);
|
||||
|
||||
#if HAS_SCREEN && \
|
||||
!defined(MESHTASTIC_EXCLUDE_SCREEN) // Todo: migrate this display code back into Screen class, and observe bluetoothStatus
|
||||
if (screen) {
|
||||
screen->startAlert([](OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) -> void {
|
||||
char btPIN[16] = "888888";
|
||||
snprintf(btPIN, sizeof(btPIN), "%06u", configuredPasskey);
|
||||
int x_offset = display->width() / 2;
|
||||
int y_offset = display->height() <= 80 ? 0 : 12;
|
||||
display->setTextAlignment(TEXT_ALIGN_CENTER);
|
||||
display->setFont(FONT_MEDIUM);
|
||||
display->drawString(x_offset + x, y_offset + y, "Bluetooth");
|
||||
#if HAS_SCREEN && \
|
||||
!defined(MESHTASTIC_EXCLUDE_SCREEN) // Todo: migrate this display code back
|
||||
// into Screen class, and observe
|
||||
// bluetoothStatus
|
||||
if (screen) {
|
||||
screen->startAlert([](OLEDDisplay *display, OLEDDisplayUiState *state,
|
||||
int16_t x, int16_t y) -> void {
|
||||
char btPIN[16] = "888888";
|
||||
snprintf(btPIN, sizeof(btPIN), "%06u", configuredPasskey);
|
||||
int x_offset = display->width() / 2;
|
||||
int y_offset = display->height() <= 80 ? 0 : 12;
|
||||
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_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_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);
|
||||
});
|
||||
}
|
||||
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
|
||||
if (match_request) {
|
||||
uint32_t start_time = millis();
|
||||
while (millis() < start_time + 30000) {
|
||||
if (!Bluefruit.connected(conn_handle))
|
||||
break;
|
||||
}
|
||||
if (match_request) {
|
||||
uint32_t start_time = millis();
|
||||
while (millis() < start_time + 30000) {
|
||||
if (!Bluefruit.connected(conn_handle))
|
||||
break;
|
||||
}
|
||||
LOG_INFO("BLE passkey pair: match_request=%i", match_request);
|
||||
return true;
|
||||
}
|
||||
LOG_INFO("BLE passkey pair: match_request=%i", match_request);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Actively refuse new BLE pairings
|
||||
// After clearing bonds (at factory reset), clients seem initially able to attempt to re-pair, even with advertising disabled.
|
||||
// On NRF52Bluetooth::shutdown, we change the pairing callback to this method, to aggressively refuse any connection attempts.
|
||||
bool NRF52Bluetooth::onUnwantedPairing(uint16_t conn_handle, uint8_t const passkey[6], bool match_request)
|
||||
{
|
||||
NRF52Bluetooth::disconnect();
|
||||
return false;
|
||||
// After clearing bonds (at factory reset), clients seem initially able to
|
||||
// attempt to re-pair, even with advertising disabled. On
|
||||
// NRF52Bluetooth::shutdown, we change the pairing callback to this method, to
|
||||
// aggressively refuse any connection attempts.
|
||||
bool NRF52Bluetooth::onUnwantedPairing(uint16_t conn_handle,
|
||||
uint8_t const passkey[6],
|
||||
bool match_request) {
|
||||
NRF52Bluetooth::disconnect();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Disconnect any BLE connections
|
||||
void NRF52Bluetooth::disconnect()
|
||||
{
|
||||
uint8_t connection_num = Bluefruit.connected();
|
||||
if (connection_num) {
|
||||
// Close all connections. We're only expecting one.
|
||||
for (uint8_t i = 0; i < connection_num; i++)
|
||||
Bluefruit.disconnect(i);
|
||||
void NRF52Bluetooth::disconnect() {
|
||||
uint8_t connection_num = Bluefruit.connected();
|
||||
if (connection_num) {
|
||||
// Close all connections. We're only expecting one.
|
||||
for (uint8_t i = 0; i < connection_num; i++)
|
||||
Bluefruit.disconnect(i);
|
||||
|
||||
// Wait for disconnection
|
||||
while (Bluefruit.connected())
|
||||
yield();
|
||||
// Wait for disconnection
|
||||
while (Bluefruit.connected())
|
||||
yield();
|
||||
|
||||
LOG_INFO("Ended BLE connection");
|
||||
}
|
||||
LOG_INFO("Ended BLE connection");
|
||||
}
|
||||
}
|
||||
|
||||
void NRF52Bluetooth::onPairingCompleted(uint16_t conn_handle, uint8_t auth_status)
|
||||
{
|
||||
if (auth_status == BLE_GAP_SEC_STATUS_SUCCESS) {
|
||||
LOG_INFO("BLE pair success");
|
||||
meshtastic::BluetoothStatus newConnectedStatus(meshtastic::BluetoothStatus::ConnectionState::CONNECTED);
|
||||
bluetoothStatus->updateStatus(&newConnectedStatus);
|
||||
} else {
|
||||
LOG_INFO("BLE pair failed");
|
||||
// Notify UI (or any other interested firmware components)
|
||||
meshtastic::BluetoothStatus newDisconnectedStatus(meshtastic::BluetoothStatus::ConnectionState::DISCONNECTED);
|
||||
bluetoothStatus->updateStatus(&newDisconnectedStatus);
|
||||
}
|
||||
void NRF52Bluetooth::onPairingCompleted(uint16_t conn_handle,
|
||||
uint8_t auth_status) {
|
||||
if (auth_status == BLE_GAP_SEC_STATUS_SUCCESS) {
|
||||
LOG_INFO("BLE pair success");
|
||||
meshtastic::BluetoothStatus newConnectedStatus(
|
||||
meshtastic::BluetoothStatus::ConnectionState::CONNECTED);
|
||||
bluetoothStatus->updateStatus(&newConnectedStatus);
|
||||
} else {
|
||||
LOG_INFO("BLE pair failed");
|
||||
// Notify UI (or any other interested firmware components)
|
||||
meshtastic::BluetoothStatus newDisconnectedStatus(
|
||||
meshtastic::BluetoothStatus::ConnectionState::DISCONNECTED);
|
||||
bluetoothStatus->updateStatus(&newDisconnectedStatus);
|
||||
}
|
||||
|
||||
// Todo: migrate this display code back into Screen class, and observe bluetoothStatus
|
||||
if (screen) {
|
||||
screen->endAlert();
|
||||
}
|
||||
// Todo: migrate this display code back into Screen class, and observe
|
||||
// bluetoothStatus
|
||||
if (screen) {
|
||||
screen->endAlert();
|
||||
}
|
||||
}
|
||||
|
||||
void NRF52Bluetooth::sendLog(const uint8_t *logMessage, size_t length)
|
||||
{
|
||||
if (!isConnected() || length > 512)
|
||||
return;
|
||||
if (logRadio.indicateEnabled())
|
||||
logRadio.indicate(logMessage, (uint16_t)length);
|
||||
else
|
||||
logRadio.notify(logMessage, (uint16_t)length);
|
||||
void NRF52Bluetooth::sendLog(const uint8_t *logMessage, size_t length) {
|
||||
if (!isConnected() || length > 512)
|
||||
return;
|
||||
if (logRadio.indicateEnabled())
|
||||
logRadio.indicate(logMessage, (uint16_t)length);
|
||||
else
|
||||
logRadio.notify(logMessage, (uint16_t)length);
|
||||
}
|
||||
@@ -38,200 +38,202 @@ void variant_shutdown() {}
|
||||
static nrfx_wdt_t nrfx_wdt = NRFX_WDT_INSTANCE(0);
|
||||
static nrfx_wdt_channel_id nrfx_wdt_channel_id_nrf52_main;
|
||||
|
||||
static inline void debugger_break(void)
|
||||
{
|
||||
__asm volatile("bkpt #0x01\n\t"
|
||||
"mov pc, lr\n\t");
|
||||
static inline void debugger_break(void) {
|
||||
__asm volatile("bkpt #0x01\n\t"
|
||||
"mov pc, lr\n\t");
|
||||
}
|
||||
|
||||
bool loopCanSleep()
|
||||
{
|
||||
// turn off sleep only while connected via USB
|
||||
// return true;
|
||||
return !Serial; // the bool operator on the nrf52 serial class returns true if connected to a PC currently
|
||||
// return !(TinyUSBDevice.mounted() && !TinyUSBDevice.suspended());
|
||||
bool loopCanSleep() {
|
||||
// turn off sleep only while connected via USB
|
||||
// return true;
|
||||
return !Serial; // the bool operator on the nrf52 serial class returns true if
|
||||
// connected to a PC currently
|
||||
// return !(TinyUSBDevice.mounted() && !TinyUSBDevice.suspended());
|
||||
}
|
||||
|
||||
// handle standard gcc assert failures
|
||||
void __attribute__((noreturn)) __assert_func(const char *file, int line, const char *func, const char *failedexpr)
|
||||
{
|
||||
LOG_ERROR("assert failed %s: %d, %s, test=%s", file, line, func, failedexpr);
|
||||
// debugger_break(); FIXME doesn't work, possibly not for segger
|
||||
// Reboot cpu
|
||||
NVIC_SystemReset();
|
||||
void __attribute__((noreturn)) __assert_func(const char *file, int line,
|
||||
const char *func,
|
||||
const char *failedexpr) {
|
||||
LOG_ERROR("assert failed %s: %d, %s, test=%s", file, line, func, failedexpr);
|
||||
// debugger_break(); FIXME doesn't work, possibly not for segger
|
||||
// Reboot cpu
|
||||
NVIC_SystemReset();
|
||||
}
|
||||
|
||||
void getMacAddr(uint8_t *dmac)
|
||||
{
|
||||
const uint8_t *src = (const uint8_t *)NRF_FICR->DEVICEADDR;
|
||||
dmac[5] = src[0];
|
||||
dmac[4] = src[1];
|
||||
dmac[3] = src[2];
|
||||
dmac[2] = src[3];
|
||||
dmac[1] = src[4];
|
||||
dmac[0] = src[5] | 0xc0; // MSB high two bits get set elsewhere in the bluetooth stack
|
||||
void getMacAddr(uint8_t *dmac) {
|
||||
const uint8_t *src = (const uint8_t *)NRF_FICR->DEVICEADDR;
|
||||
dmac[5] = src[0];
|
||||
dmac[4] = src[1];
|
||||
dmac[3] = src[2];
|
||||
dmac[2] = src[3];
|
||||
dmac[1] = src[4];
|
||||
dmac[0] = src[5] |
|
||||
0xc0; // MSB high two bits get set elsewhere in the bluetooth stack
|
||||
}
|
||||
|
||||
static void initBrownout()
|
||||
{
|
||||
auto vccthresh = POWER_POFCON_THRESHOLD_V24;
|
||||
static void initBrownout() {
|
||||
auto vccthresh = POWER_POFCON_THRESHOLD_V24;
|
||||
|
||||
auto err_code = sd_power_pof_enable(POWER_POFCON_POF_Enabled);
|
||||
assert(err_code == NRF_SUCCESS);
|
||||
auto err_code = sd_power_pof_enable(POWER_POFCON_POF_Enabled);
|
||||
assert(err_code == NRF_SUCCESS);
|
||||
|
||||
err_code = sd_power_pof_threshold_set(vccthresh);
|
||||
assert(err_code == NRF_SUCCESS);
|
||||
err_code = sd_power_pof_threshold_set(vccthresh);
|
||||
assert(err_code == NRF_SUCCESS);
|
||||
|
||||
// We don't bother with setting up brownout if soft device is disabled - because during production we always use softdevice
|
||||
// We don't bother with setting up brownout if soft device is disabled -
|
||||
// because during production we always use softdevice
|
||||
}
|
||||
|
||||
// This is a public global so that the debugger can set it to false automatically from our gdbinit
|
||||
// This is a public global so that the debugger can set it to false
|
||||
// automatically from our gdbinit
|
||||
bool useSoftDevice = true; // Set to false for easier debugging
|
||||
|
||||
#if !MESHTASTIC_EXCLUDE_BLUETOOTH
|
||||
void setBluetoothEnable(bool enable)
|
||||
{
|
||||
// For debugging use: don't use bluetooth
|
||||
if (!useSoftDevice) {
|
||||
if (enable)
|
||||
LOG_INFO("Disable NRF52 BLUETOOTH WHILE DEBUGGING");
|
||||
return;
|
||||
}
|
||||
void setBluetoothEnable(bool enable) {
|
||||
// For debugging use: don't use bluetooth
|
||||
if (!useSoftDevice) {
|
||||
if (enable)
|
||||
LOG_INFO("Disable NRF52 BLUETOOTH WHILE DEBUGGING");
|
||||
return;
|
||||
}
|
||||
|
||||
// If user disabled bluetooth: init then disable advertising & reduce power
|
||||
// Workaround. Avoid issue where device hangs several days after boot..
|
||||
// Allegedly, no significant increase in power consumption
|
||||
if (!config.bluetooth.enabled) {
|
||||
static bool initialized = false;
|
||||
if (!initialized) {
|
||||
nrf52Bluetooth = new NRF52Bluetooth();
|
||||
nrf52Bluetooth->startDisabled();
|
||||
initBrownout();
|
||||
initialized = true;
|
||||
}
|
||||
return;
|
||||
// If user disabled bluetooth: init then disable advertising & reduce power
|
||||
// Workaround. Avoid issue where device hangs several days after boot..
|
||||
// Allegedly, no significant increase in power consumption
|
||||
if (!config.bluetooth.enabled) {
|
||||
static bool initialized = false;
|
||||
if (!initialized) {
|
||||
nrf52Bluetooth = new NRF52Bluetooth();
|
||||
nrf52Bluetooth->startDisabled();
|
||||
initBrownout();
|
||||
initialized = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (enable) {
|
||||
powerMon->setState(meshtastic_PowerMon_State_BT_On);
|
||||
if (enable) {
|
||||
powerMon->setState(meshtastic_PowerMon_State_BT_On);
|
||||
|
||||
// If not yet set-up
|
||||
if (!nrf52Bluetooth) {
|
||||
LOG_DEBUG("Init NRF52 Bluetooth");
|
||||
nrf52Bluetooth = new NRF52Bluetooth();
|
||||
nrf52Bluetooth->setup();
|
||||
// If not yet set-up
|
||||
if (!nrf52Bluetooth) {
|
||||
LOG_DEBUG("Init NRF52 Bluetooth");
|
||||
nrf52Bluetooth = new NRF52Bluetooth();
|
||||
nrf52Bluetooth->setup();
|
||||
|
||||
// We delay brownout init until after BLE because BLE starts soft device
|
||||
initBrownout();
|
||||
}
|
||||
// Already setup, apparently
|
||||
else
|
||||
nrf52Bluetooth->resumeAdvertising();
|
||||
}
|
||||
// Disable (if previously set-up)
|
||||
else if (nrf52Bluetooth) {
|
||||
powerMon->clearState(meshtastic_PowerMon_State_BT_On);
|
||||
nrf52Bluetooth->shutdown();
|
||||
// We delay brownout init until after BLE because BLE starts soft device
|
||||
initBrownout();
|
||||
}
|
||||
// Already setup, apparently
|
||||
else
|
||||
nrf52Bluetooth->resumeAdvertising();
|
||||
}
|
||||
// Disable (if previously set-up)
|
||||
else if (nrf52Bluetooth) {
|
||||
powerMon->clearState(meshtastic_PowerMon_State_BT_On);
|
||||
nrf52Bluetooth->shutdown();
|
||||
}
|
||||
}
|
||||
#else
|
||||
#warning NRF52 "Bluetooth disable" workaround does not apply to builds with MESHTASTIC_EXCLUDE_BLUETOOTH
|
||||
void setBluetoothEnable(bool enable) {}
|
||||
#endif
|
||||
/**
|
||||
* Override printf to use the SEGGER output library (note - this does not effect the printf method on the debug console)
|
||||
* Override printf to use the SEGGER output library (note - this does not effect
|
||||
* the printf method on the debug console)
|
||||
*/
|
||||
int printf(const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
auto res = SEGGER_RTT_vprintf(0, fmt, &args);
|
||||
va_end(args);
|
||||
return res;
|
||||
int printf(const char *fmt, ...) {
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
auto res = SEGGER_RTT_vprintf(0, fmt, &args);
|
||||
va_end(args);
|
||||
return res;
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
namespace {
|
||||
constexpr uint8_t NRF52_MAGIC_LFS_IS_CORRUPT = 0xF5;
|
||||
constexpr uint32_t MULTIPLE_CORRUPTION_DELAY_MILLIS = 20 * 60 * 1000;
|
||||
static unsigned long millis_until_formatting_again = 0;
|
||||
|
||||
// Report the critical error from loop(), giving a chance for the screen to be initialized first.
|
||||
inline void reportLittleFSCorruptionOnce()
|
||||
{
|
||||
static bool report_corruption = !!millis_until_formatting_again;
|
||||
if (report_corruption) {
|
||||
report_corruption = false;
|
||||
RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_FLASH_CORRUPTION_UNRECOVERABLE);
|
||||
}
|
||||
// Report the critical error from loop(), giving a chance for the screen to be
|
||||
// initialized first.
|
||||
inline void reportLittleFSCorruptionOnce() {
|
||||
static bool report_corruption = !!millis_until_formatting_again;
|
||||
if (report_corruption) {
|
||||
report_corruption = false;
|
||||
RECORD_CRITICALERROR(
|
||||
meshtastic_CriticalErrorCode_FLASH_CORRUPTION_UNRECOVERABLE);
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void preFSBegin()
|
||||
{
|
||||
// The GPREGRET register keeps its value across warm boots. Check that this is a warm boot and, if GPREGRET
|
||||
// is set to NRF52_MAGIC_LFS_IS_CORRUPT, format LittleFS.
|
||||
if (!(NRF_POWER->RESETREAS == 0 && NRF_POWER->GPREGRET == NRF52_MAGIC_LFS_IS_CORRUPT))
|
||||
return;
|
||||
NRF_POWER->GPREGRET = 0;
|
||||
millis_until_formatting_again = millis() + MULTIPLE_CORRUPTION_DELAY_MILLIS;
|
||||
InternalFS.format();
|
||||
LOG_INFO("LittleFS format complete; restoring default settings");
|
||||
void preFSBegin() {
|
||||
// The GPREGRET register keeps its value across warm boots. Check that this is
|
||||
// a warm boot and, if GPREGRET is set to NRF52_MAGIC_LFS_IS_CORRUPT, format
|
||||
// LittleFS.
|
||||
if (!(NRF_POWER->RESETREAS == 0 &&
|
||||
NRF_POWER->GPREGRET == NRF52_MAGIC_LFS_IS_CORRUPT))
|
||||
return;
|
||||
NRF_POWER->GPREGRET = 0;
|
||||
millis_until_formatting_again = millis() + MULTIPLE_CORRUPTION_DELAY_MILLIS;
|
||||
InternalFS.format();
|
||||
LOG_INFO("LittleFS format complete; restoring default settings");
|
||||
}
|
||||
|
||||
extern "C" void lfs_assert(const char *reason)
|
||||
{
|
||||
LOG_ERROR("LittleFS corruption detected: %s", reason);
|
||||
if (millis_until_formatting_again > millis()) {
|
||||
RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_FLASH_CORRUPTION_UNRECOVERABLE);
|
||||
const long millis_remain = millis_until_formatting_again - millis();
|
||||
LOG_WARN("Pausing %d seconds to avoid wear on flash storage", millis_remain / 1000);
|
||||
delay(millis_remain);
|
||||
}
|
||||
LOG_INFO("Rebooting to format LittleFS");
|
||||
delay(500); // Give the serial port a bit of time to output that last message.
|
||||
// Try setting GPREGRET with the SoftDevice first. If that fails (perhaps because the SD hasn't been initialize yet) then set
|
||||
// NRF_POWER->GPREGRET directly.
|
||||
if (!(sd_power_gpregret_clr(0, 0xFF) == NRF_SUCCESS && sd_power_gpregret_set(0, NRF52_MAGIC_LFS_IS_CORRUPT) == NRF_SUCCESS)) {
|
||||
NRF_POWER->GPREGRET = NRF52_MAGIC_LFS_IS_CORRUPT;
|
||||
}
|
||||
NVIC_SystemReset();
|
||||
extern "C" void lfs_assert(const char *reason) {
|
||||
LOG_ERROR("LittleFS corruption detected: %s", reason);
|
||||
if (millis_until_formatting_again > millis()) {
|
||||
RECORD_CRITICALERROR(
|
||||
meshtastic_CriticalErrorCode_FLASH_CORRUPTION_UNRECOVERABLE);
|
||||
const long millis_remain = millis_until_formatting_again - millis();
|
||||
LOG_WARN("Pausing %d seconds to avoid wear on flash storage",
|
||||
millis_remain / 1000);
|
||||
delay(millis_remain);
|
||||
}
|
||||
LOG_INFO("Rebooting to format LittleFS");
|
||||
delay(500); // Give the serial port a bit of time to output that last message.
|
||||
// Try setting GPREGRET with the SoftDevice first. If that fails (perhaps
|
||||
// because the SD hasn't been initialize yet) then set NRF_POWER->GPREGRET
|
||||
// directly.
|
||||
if (!(sd_power_gpregret_clr(0, 0xFF) == NRF_SUCCESS &&
|
||||
sd_power_gpregret_set(0, NRF52_MAGIC_LFS_IS_CORRUPT) == NRF_SUCCESS)) {
|
||||
NRF_POWER->GPREGRET = NRF52_MAGIC_LFS_IS_CORRUPT;
|
||||
}
|
||||
NVIC_SystemReset();
|
||||
}
|
||||
|
||||
void checkSDEvents()
|
||||
{
|
||||
if (useSoftDevice) {
|
||||
uint32_t evt;
|
||||
while (NRF_SUCCESS == sd_evt_get(&evt)) {
|
||||
switch (evt) {
|
||||
case NRF_EVT_POWER_FAILURE_WARNING:
|
||||
RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_BROWNOUT);
|
||||
break;
|
||||
void checkSDEvents() {
|
||||
if (useSoftDevice) {
|
||||
uint32_t evt;
|
||||
while (NRF_SUCCESS == sd_evt_get(&evt)) {
|
||||
switch (evt) {
|
||||
case NRF_EVT_POWER_FAILURE_WARNING:
|
||||
RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_BROWNOUT);
|
||||
break;
|
||||
|
||||
default:
|
||||
LOG_DEBUG("Unexpected SDevt %d", evt);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (NRF_POWER->EVENTS_POFWARN)
|
||||
RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_BROWNOUT);
|
||||
default:
|
||||
LOG_DEBUG("Unexpected SDevt %d", evt);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (NRF_POWER->EVENTS_POFWARN)
|
||||
RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_BROWNOUT);
|
||||
}
|
||||
}
|
||||
|
||||
void nrf52Loop()
|
||||
{
|
||||
{
|
||||
static bool watchdog_running = false;
|
||||
if (!watchdog_running) {
|
||||
nrfx_wdt_enable(&nrfx_wdt);
|
||||
watchdog_running = true;
|
||||
}
|
||||
void nrf52Loop() {
|
||||
{
|
||||
static bool watchdog_running = false;
|
||||
if (!watchdog_running) {
|
||||
nrfx_wdt_enable(&nrfx_wdt);
|
||||
watchdog_running = true;
|
||||
}
|
||||
nrfx_wdt_channel_feed(&nrfx_wdt, nrfx_wdt_channel_id_nrf52_main);
|
||||
}
|
||||
nrfx_wdt_channel_feed(&nrfx_wdt, nrfx_wdt_channel_id_nrf52_main);
|
||||
|
||||
checkSDEvents();
|
||||
reportLittleFSCorruptionOnce();
|
||||
checkSDEvents();
|
||||
reportLittleFSCorruptionOnce();
|
||||
}
|
||||
|
||||
#ifdef USE_SEMIHOSTING
|
||||
@@ -240,263 +242,277 @@ void nrf52Loop()
|
||||
|
||||
/**
|
||||
* Note: this variable is in BSS and therfore false by default. But the gdbinit
|
||||
* file will be installing a temporary breakpoint that changes wantSemihost to true.
|
||||
* file will be installing a temporary breakpoint that changes wantSemihost to
|
||||
* true.
|
||||
*/
|
||||
bool wantSemihost;
|
||||
|
||||
/**
|
||||
* Turn on semihosting if the ICE debugger wants it.
|
||||
*/
|
||||
void nrf52InitSemiHosting()
|
||||
{
|
||||
if (wantSemihost) {
|
||||
static SemihostingStream semiStream;
|
||||
// We must dynamically alloc because the constructor does semihost operations which
|
||||
// would crash any load not talking to a debugger
|
||||
semiStream.open();
|
||||
semiStream.println("Semihosting starts!");
|
||||
// Redirect our serial output to instead go via the ICE port
|
||||
console->setDestination(&semiStream);
|
||||
}
|
||||
void nrf52InitSemiHosting() {
|
||||
if (wantSemihost) {
|
||||
static SemihostingStream semiStream;
|
||||
// We must dynamically alloc because the constructor does semihost
|
||||
// operations which would crash any load not talking to a debugger
|
||||
semiStream.open();
|
||||
semiStream.println("Semihosting starts!");
|
||||
// Redirect our serial output to instead go via the ICE port
|
||||
console->setDestination(&semiStream);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void nrf52Setup()
|
||||
{
|
||||
void nrf52Setup() {
|
||||
#ifdef ADC_V
|
||||
pinMode(ADC_V, INPUT);
|
||||
pinMode(ADC_V, INPUT);
|
||||
#endif
|
||||
|
||||
uint32_t why = NRF_POWER->RESETREAS;
|
||||
// per
|
||||
// https://infocenter.nordicsemi.com/index.jsp?topic=%2Fcom.nordic.infocenter.nrf52832.ps.v1.1%2Fpower.html
|
||||
LOG_DEBUG("Reset reason: 0x%x", why);
|
||||
uint32_t why = NRF_POWER->RESETREAS;
|
||||
// per
|
||||
// https://infocenter.nordicsemi.com/index.jsp?topic=%2Fcom.nordic.infocenter.nrf52832.ps.v1.1%2Fpower.html
|
||||
LOG_DEBUG("Reset reason: 0x%x", why);
|
||||
|
||||
#ifdef USE_SEMIHOSTING
|
||||
nrf52InitSemiHosting();
|
||||
nrf52InitSemiHosting();
|
||||
#endif
|
||||
|
||||
// Per
|
||||
// https://devzone.nordicsemi.com/nordic/nordic-blog/b/blog/posts/monitor-mode-debugging-with-j-link-and-gdbeclipse
|
||||
// This is the recommended setting for Monitor Mode Debugging
|
||||
NVIC_SetPriority(DebugMonitor_IRQn, 6UL);
|
||||
// Per
|
||||
// https://devzone.nordicsemi.com/nordic/nordic-blog/b/blog/posts/monitor-mode-debugging-with-j-link-and-gdbeclipse
|
||||
// This is the recommended setting for Monitor Mode Debugging
|
||||
NVIC_SetPriority(DebugMonitor_IRQn, 6UL);
|
||||
|
||||
#ifdef BQ25703A_ADDR
|
||||
auto *bq = new BQ25713();
|
||||
if (!bq->setup())
|
||||
LOG_ERROR("ERROR! Charge controller init failed");
|
||||
auto *bq = new BQ25713();
|
||||
if (!bq->setup())
|
||||
LOG_ERROR("ERROR! Charge controller init failed");
|
||||
#endif
|
||||
|
||||
// Init random seed
|
||||
union seedParts {
|
||||
uint32_t seed32;
|
||||
uint8_t seed8[4];
|
||||
} seed;
|
||||
nRFCrypto.begin();
|
||||
nRFCrypto.Random.generate(seed.seed8, sizeof(seed.seed8));
|
||||
LOG_DEBUG("Set random seed %u", seed.seed32);
|
||||
randomSeed(seed.seed32);
|
||||
nRFCrypto.end();
|
||||
// Init random seed
|
||||
union seedParts {
|
||||
uint32_t seed32;
|
||||
uint8_t seed8[4];
|
||||
} seed;
|
||||
nRFCrypto.begin();
|
||||
nRFCrypto.Random.generate(seed.seed8, sizeof(seed.seed8));
|
||||
LOG_DEBUG("Set random seed %u", seed.seed32);
|
||||
randomSeed(seed.seed32);
|
||||
nRFCrypto.end();
|
||||
|
||||
// Set up nrfx watchdog. Do not enable the watchdog yet (we do that
|
||||
// the first time through the main loop), so that other threads can
|
||||
// allocate their own wdt channel to protect themselves from hangs.
|
||||
nrfx_wdt_config_t wdt0_config = {
|
||||
.behaviour = NRF_WDT_BEHAVIOUR_PAUSE_SLEEP_HALT, .reload_value = APP_WATCHDOG_SECS * 1000,
|
||||
// Note: Not using wdt interrupts.
|
||||
// .interrupt_priority = NRFX_WDT_DEFAULT_CONFIG_IRQ_PRIORITY
|
||||
};
|
||||
nrfx_err_t r = nrfx_wdt_init(&nrfx_wdt, &wdt0_config,
|
||||
nullptr // Watchdog event handler, not used, we just reset.
|
||||
);
|
||||
assert(r == NRFX_SUCCESS);
|
||||
// Set up nrfx watchdog. Do not enable the watchdog yet (we do that
|
||||
// the first time through the main loop), so that other threads can
|
||||
// allocate their own wdt channel to protect themselves from hangs.
|
||||
nrfx_wdt_config_t wdt0_config = {
|
||||
.behaviour = NRF_WDT_BEHAVIOUR_PAUSE_SLEEP_HALT,
|
||||
.reload_value = APP_WATCHDOG_SECS * 1000,
|
||||
// Note: Not using wdt interrupts.
|
||||
// .interrupt_priority = NRFX_WDT_DEFAULT_CONFIG_IRQ_PRIORITY
|
||||
};
|
||||
nrfx_err_t r =
|
||||
nrfx_wdt_init(&nrfx_wdt, &wdt0_config,
|
||||
nullptr // Watchdog event handler, not used, we just reset.
|
||||
);
|
||||
assert(r == NRFX_SUCCESS);
|
||||
|
||||
r = nrfx_wdt_channel_alloc(&nrfx_wdt, &nrfx_wdt_channel_id_nrf52_main);
|
||||
assert(r == NRFX_SUCCESS);
|
||||
r = nrfx_wdt_channel_alloc(&nrfx_wdt, &nrfx_wdt_channel_id_nrf52_main);
|
||||
assert(r == NRFX_SUCCESS);
|
||||
|
||||
|
||||
// print LFCLK debug info
|
||||
// print LFCLK debug info
|
||||
|
||||
// const char *clkSource = NULL;
|
||||
|
||||
// switch (NRF_CLOCK->LFCLKSRC & 0x03){
|
||||
// case CLOCK_LFCLKSRC_SRC_Xtal:
|
||||
// clkSource = "XTAL";
|
||||
// break;
|
||||
// case CLOCK_LFCLKSRC_SRC_RC:
|
||||
/// clkSource = "RC";
|
||||
// break;
|
||||
// }
|
||||
|
||||
//LOG_DEBUG("LFCLK source: %s", clkSource);
|
||||
// switch (NRF_CLOCK->LFCLKSRC & 0x03){
|
||||
// case CLOCK_LFCLKSRC_SRC_Xtal:
|
||||
// clkSource = "XTAL";
|
||||
// break;
|
||||
// case CLOCK_LFCLKSRC_SRC_RC:
|
||||
/// clkSource = "RC";
|
||||
// break;
|
||||
// }
|
||||
|
||||
// LOG_DEBUG("LFCLK source: %s", clkSource);
|
||||
}
|
||||
|
||||
void cpuDeepSleep(uint32_t msecToWake)
|
||||
{
|
||||
// FIXME, configure RTC or button press to wake us
|
||||
// FIXME, power down SPI, I2C, RAMs
|
||||
void cpuDeepSleep(uint32_t msecToWake) {
|
||||
// FIXME, configure RTC or button press to wake us
|
||||
// FIXME, power down SPI, I2C, RAMs
|
||||
#if HAS_WIRE
|
||||
Wire.end();
|
||||
Wire.end();
|
||||
#endif
|
||||
SPI.end();
|
||||
SPI.end();
|
||||
#if SPI_INTERFACES_COUNT > 1
|
||||
SPI1.end();
|
||||
SPI1.end();
|
||||
#endif
|
||||
if (Serial) // Another check in case of disabled default serial, does nothing bad
|
||||
Serial.end(); // This may cause crashes as debug messages continue to flow.
|
||||
if (Serial) // Another check in case of disabled default serial, does nothing
|
||||
// bad
|
||||
Serial.end(); // This may cause crashes as debug messages continue to flow.
|
||||
|
||||
// This causes troubles with waking up on nrf52 (on pro-micro in particular):
|
||||
// we have no Serial1 in use on nrf52, check Serial and GPS modules.
|
||||
// This causes troubles with waking up on nrf52 (on pro-micro in
|
||||
// particular): we have no Serial1 in use on nrf52, check Serial and GPS
|
||||
// modules.
|
||||
#ifdef PIN_SERIAL1_RX
|
||||
if (Serial1) // A straightforward solution to the wake from deepsleep problem
|
||||
Serial1.end();
|
||||
if (Serial1) // A straightforward solution to the wake from deepsleep problem
|
||||
Serial1.end();
|
||||
#endif
|
||||
setBluetoothEnable(false);
|
||||
setBluetoothEnable(false);
|
||||
|
||||
#ifdef RAK4630
|
||||
#ifdef PIN_3V3_EN
|
||||
digitalWrite(PIN_3V3_EN, LOW);
|
||||
digitalWrite(PIN_3V3_EN, LOW);
|
||||
#endif
|
||||
#ifdef AQ_SET_PIN
|
||||
// RAK-12039 set pin for Air quality sensor
|
||||
digitalWrite(AQ_SET_PIN, LOW);
|
||||
// RAK-12039 set pin for Air quality sensor
|
||||
digitalWrite(AQ_SET_PIN, LOW);
|
||||
#endif
|
||||
#ifdef RAK14014
|
||||
// GPIO restores input status, otherwise there will be leakage current
|
||||
nrf_gpio_cfg_default(TFT_BL);
|
||||
nrf_gpio_cfg_default(TFT_DC);
|
||||
nrf_gpio_cfg_default(TFT_CS);
|
||||
nrf_gpio_cfg_default(TFT_SCLK);
|
||||
nrf_gpio_cfg_default(TFT_MOSI);
|
||||
nrf_gpio_cfg_default(TFT_MISO);
|
||||
nrf_gpio_cfg_default(SCREEN_TOUCH_INT);
|
||||
nrf_gpio_cfg_default(WB_I2C1_SCL);
|
||||
nrf_gpio_cfg_default(WB_I2C1_SDA);
|
||||
// GPIO restores input status, otherwise there will be leakage current
|
||||
nrf_gpio_cfg_default(TFT_BL);
|
||||
nrf_gpio_cfg_default(TFT_DC);
|
||||
nrf_gpio_cfg_default(TFT_CS);
|
||||
nrf_gpio_cfg_default(TFT_SCLK);
|
||||
nrf_gpio_cfg_default(TFT_MOSI);
|
||||
nrf_gpio_cfg_default(TFT_MISO);
|
||||
nrf_gpio_cfg_default(SCREEN_TOUCH_INT);
|
||||
nrf_gpio_cfg_default(WB_I2C1_SCL);
|
||||
nrf_gpio_cfg_default(WB_I2C1_SDA);
|
||||
|
||||
// nrf_gpio_cfg_default(WB_I2C2_SCL);
|
||||
// nrf_gpio_cfg_default(WB_I2C2_SDA);
|
||||
// nrf_gpio_cfg_default(WB_I2C2_SCL);
|
||||
// nrf_gpio_cfg_default(WB_I2C2_SDA);
|
||||
#endif
|
||||
#endif
|
||||
#ifdef MESHLINK
|
||||
#ifdef PIN_WD_EN
|
||||
digitalWrite(PIN_WD_EN, LOW);
|
||||
digitalWrite(PIN_WD_EN, LOW);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(HELTEC_MESH_NODE_T114) || defined(HELTEC_MESH_SOLAR)
|
||||
nrf_gpio_cfg_default(PIN_GPS_PPS);
|
||||
detachInterrupt(PIN_GPS_PPS);
|
||||
detachInterrupt(PIN_BUTTON1);
|
||||
nrf_gpio_cfg_default(PIN_GPS_PPS);
|
||||
detachInterrupt(PIN_GPS_PPS);
|
||||
detachInterrupt(PIN_BUTTON1);
|
||||
#endif
|
||||
|
||||
#ifdef ELECROW_ThinkNode_M1
|
||||
for (int pin = 0; pin < 48; pin++) {
|
||||
if (pin == 17 || pin == 19 || pin == 20 || pin == 22 || pin == 23 || pin == 24 || pin == 25 || pin == 9 || pin == 10 ||
|
||||
pin == PIN_BUTTON1 || pin == PIN_BUTTON2) {
|
||||
continue;
|
||||
}
|
||||
pinMode(pin, OUTPUT);
|
||||
for (int pin = 0; pin < 48; pin++) {
|
||||
if (pin == 17 || pin == 19 || pin == 20 || pin == 22 || pin == 23 ||
|
||||
pin == 24 || pin == 25 || pin == 9 || pin == 10 || pin == PIN_BUTTON1 ||
|
||||
pin == PIN_BUTTON2) {
|
||||
continue;
|
||||
}
|
||||
for (int pin = 0; pin < 48; pin++) {
|
||||
if (pin == 17 || pin == 19 || pin == 20 || pin == 22 || pin == 23 || pin == 24 || pin == 25 || pin == 9 || pin == 10 ||
|
||||
pin == PIN_BUTTON1 || pin == PIN_BUTTON2) {
|
||||
continue;
|
||||
}
|
||||
digitalWrite(pin, LOW);
|
||||
pinMode(pin, OUTPUT);
|
||||
}
|
||||
for (int pin = 0; pin < 48; pin++) {
|
||||
if (pin == 17 || pin == 19 || pin == 20 || pin == 22 || pin == 23 ||
|
||||
pin == 24 || pin == 25 || pin == 9 || pin == 10 || pin == PIN_BUTTON1 ||
|
||||
pin == PIN_BUTTON2) {
|
||||
continue;
|
||||
}
|
||||
for (int pin = 0; pin < 48; pin++) {
|
||||
if (pin == 17 || pin == 19 || pin == 20 || pin == 22 || pin == 23 || pin == 24 || pin == 25 || pin == 9 || pin == 10 ||
|
||||
pin == PIN_BUTTON1 || pin == PIN_BUTTON2) {
|
||||
continue;
|
||||
}
|
||||
NRF_GPIO->DIRCLR = (1 << pin);
|
||||
digitalWrite(pin, LOW);
|
||||
}
|
||||
for (int pin = 0; pin < 48; pin++) {
|
||||
if (pin == 17 || pin == 19 || pin == 20 || pin == 22 || pin == 23 ||
|
||||
pin == 24 || pin == 25 || pin == 9 || pin == 10 || pin == PIN_BUTTON1 ||
|
||||
pin == PIN_BUTTON2) {
|
||||
continue;
|
||||
}
|
||||
NRF_GPIO->DIRCLR = (1 << pin);
|
||||
}
|
||||
#endif
|
||||
variant_shutdown();
|
||||
variant_shutdown();
|
||||
|
||||
// Sleepy trackers or sensors can low power "sleep"
|
||||
// Don't enter this if we're sleeping portMAX_DELAY, since that's a shutdown event
|
||||
if (msecToWake != portMAX_DELAY &&
|
||||
(IS_ONE_OF(config.device.role, meshtastic_Config_DeviceConfig_Role_TRACKER,
|
||||
meshtastic_Config_DeviceConfig_Role_TAK_TRACKER, meshtastic_Config_DeviceConfig_Role_SENSOR) &&
|
||||
config.power.is_power_saving == true)) {
|
||||
sd_power_mode_set(NRF_POWER_MODE_LOWPWR);
|
||||
delay(msecToWake);
|
||||
NVIC_SystemReset();
|
||||
} else {
|
||||
// Resume on user button press
|
||||
// https://github.com/lyusupov/SoftRF/blob/81c519ca75693b696752235d559e881f2e0511ee/software/firmware/source/SoftRF/src/platform/nRF52.cpp#L1738
|
||||
constexpr uint32_t DFU_MAGIC_SKIP = 0x6d;
|
||||
sd_power_gpregret_clr(0, 0xFF); // Clear the register before setting a new values in it for stability reasons
|
||||
sd_power_gpregret_set(0, DFU_MAGIC_SKIP); // Equivalent NRF_POWER->GPREGRET = DFU_MAGIC_SKIP
|
||||
// Sleepy trackers or sensors can low power "sleep"
|
||||
// Don't enter this if we're sleeping portMAX_DELAY, since that's a shutdown
|
||||
// event
|
||||
if (msecToWake != portMAX_DELAY &&
|
||||
(IS_ONE_OF(config.device.role,
|
||||
meshtastic_Config_DeviceConfig_Role_TRACKER,
|
||||
meshtastic_Config_DeviceConfig_Role_TAK_TRACKER,
|
||||
meshtastic_Config_DeviceConfig_Role_SENSOR) &&
|
||||
config.power.is_power_saving == true)) {
|
||||
sd_power_mode_set(NRF_POWER_MODE_LOWPWR);
|
||||
delay(msecToWake);
|
||||
NVIC_SystemReset();
|
||||
} else {
|
||||
// Resume on user button press
|
||||
// https://github.com/lyusupov/SoftRF/blob/81c519ca75693b696752235d559e881f2e0511ee/software/firmware/source/SoftRF/src/platform/nRF52.cpp#L1738
|
||||
constexpr uint32_t DFU_MAGIC_SKIP = 0x6d;
|
||||
sd_power_gpregret_clr(0, 0xFF); // Clear the register before setting a new
|
||||
// values in it for stability reasons
|
||||
sd_power_gpregret_set(
|
||||
0, DFU_MAGIC_SKIP); // Equivalent NRF_POWER->GPREGRET = DFU_MAGIC_SKIP
|
||||
|
||||
// FIXME, use system off mode with ram retention for key state?
|
||||
// FIXME, use non-init RAM per
|
||||
// https://devzone.nordicsemi.com/f/nordic-q-a/48919/ram-retention-settings-with-softdevice-enabled
|
||||
// FIXME, use system off mode with ram retention for key state?
|
||||
// FIXME, use non-init RAM per
|
||||
// https://devzone.nordicsemi.com/f/nordic-q-a/48919/ram-retention-settings-with-softdevice-enabled
|
||||
|
||||
#ifdef ELECROW_ThinkNode_M1
|
||||
nrf_gpio_cfg_input(PIN_BUTTON1, NRF_GPIO_PIN_PULLUP); // Configure the pin to be woken up as an input
|
||||
nrf_gpio_pin_sense_t sense = NRF_GPIO_PIN_SENSE_LOW;
|
||||
nrf_gpio_cfg_sense_set(PIN_BUTTON1, sense);
|
||||
nrf_gpio_cfg_input(
|
||||
PIN_BUTTON1,
|
||||
NRF_GPIO_PIN_PULLUP); // Configure the pin to be woken up as an input
|
||||
nrf_gpio_pin_sense_t sense = NRF_GPIO_PIN_SENSE_LOW;
|
||||
nrf_gpio_cfg_sense_set(PIN_BUTTON1, sense);
|
||||
|
||||
nrf_gpio_cfg_input(PIN_BUTTON2, NRF_GPIO_PIN_PULLUP);
|
||||
nrf_gpio_pin_sense_t sense1 = NRF_GPIO_PIN_SENSE_LOW;
|
||||
nrf_gpio_cfg_sense_set(PIN_BUTTON2, sense1);
|
||||
nrf_gpio_cfg_input(PIN_BUTTON2, NRF_GPIO_PIN_PULLUP);
|
||||
nrf_gpio_pin_sense_t sense1 = NRF_GPIO_PIN_SENSE_LOW;
|
||||
nrf_gpio_cfg_sense_set(PIN_BUTTON2, sense1);
|
||||
#endif
|
||||
|
||||
#ifdef PROMICRO_DIY_TCXO
|
||||
nrf_gpio_cfg_input(BUTTON_PIN, NRF_GPIO_PIN_PULLUP); // Enable internal pull-up on the button pin
|
||||
nrf_gpio_pin_sense_t sense = NRF_GPIO_PIN_SENSE_LOW; // Configure SENSE signal on low edge
|
||||
nrf_gpio_cfg_sense_set(BUTTON_PIN, sense); // Apply SENSE to wake up the device from the deep sleep
|
||||
nrf_gpio_cfg_input(
|
||||
BUTTON_PIN,
|
||||
NRF_GPIO_PIN_PULLUP); // Enable internal pull-up on the button pin
|
||||
nrf_gpio_pin_sense_t sense =
|
||||
NRF_GPIO_PIN_SENSE_LOW; // Configure SENSE signal on low edge
|
||||
nrf_gpio_cfg_sense_set(
|
||||
BUTTON_PIN,
|
||||
sense); // Apply SENSE to wake up the device from the deep sleep
|
||||
#endif
|
||||
|
||||
#ifdef BATTERY_LPCOMP_INPUT
|
||||
// Wake up if power rises again
|
||||
nrf_lpcomp_config_t c;
|
||||
c.reference = BATTERY_LPCOMP_THRESHOLD;
|
||||
c.detection = NRF_LPCOMP_DETECT_UP;
|
||||
c.hyst = NRF_LPCOMP_HYST_NOHYST;
|
||||
nrf_lpcomp_configure(NRF_LPCOMP, &c);
|
||||
nrf_lpcomp_input_select(NRF_LPCOMP, BATTERY_LPCOMP_INPUT);
|
||||
nrf_lpcomp_enable(NRF_LPCOMP);
|
||||
// Wake up if power rises again
|
||||
nrf_lpcomp_config_t c;
|
||||
c.reference = BATTERY_LPCOMP_THRESHOLD;
|
||||
c.detection = NRF_LPCOMP_DETECT_UP;
|
||||
c.hyst = NRF_LPCOMP_HYST_NOHYST;
|
||||
nrf_lpcomp_configure(NRF_LPCOMP, &c);
|
||||
nrf_lpcomp_input_select(NRF_LPCOMP, BATTERY_LPCOMP_INPUT);
|
||||
nrf_lpcomp_enable(NRF_LPCOMP);
|
||||
|
||||
battery_adcEnable();
|
||||
battery_adcEnable();
|
||||
|
||||
nrf_lpcomp_task_trigger(NRF_LPCOMP, NRF_LPCOMP_TASK_START);
|
||||
while (!nrf_lpcomp_event_check(NRF_LPCOMP, NRF_LPCOMP_EVENT_READY))
|
||||
;
|
||||
nrf_lpcomp_task_trigger(NRF_LPCOMP, NRF_LPCOMP_TASK_START);
|
||||
while (!nrf_lpcomp_event_check(NRF_LPCOMP, NRF_LPCOMP_EVENT_READY))
|
||||
;
|
||||
#endif
|
||||
|
||||
auto ok = sd_power_system_off();
|
||||
if (ok != NRF_SUCCESS) {
|
||||
LOG_ERROR("FIXME: Ignoring soft device (EasyDMA pending?) and forcing system-off!");
|
||||
NRF_POWER->SYSTEMOFF = 1;
|
||||
}
|
||||
auto ok = sd_power_system_off();
|
||||
if (ok != NRF_SUCCESS) {
|
||||
LOG_ERROR("FIXME: Ignoring soft device (EasyDMA pending?) and forcing "
|
||||
"system-off!");
|
||||
NRF_POWER->SYSTEMOFF = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// The following code should not be run, because we are off
|
||||
while (1) {
|
||||
delay(5000);
|
||||
LOG_DEBUG(".");
|
||||
}
|
||||
// The following code should not be run, because we are off
|
||||
while (1) {
|
||||
delay(5000);
|
||||
LOG_DEBUG(".");
|
||||
}
|
||||
}
|
||||
|
||||
void clearBonds()
|
||||
{
|
||||
if (!nrf52Bluetooth) {
|
||||
nrf52Bluetooth = new NRF52Bluetooth();
|
||||
nrf52Bluetooth->setup();
|
||||
}
|
||||
nrf52Bluetooth->clearBonds();
|
||||
void clearBonds() {
|
||||
if (!nrf52Bluetooth) {
|
||||
nrf52Bluetooth = new NRF52Bluetooth();
|
||||
nrf52Bluetooth->setup();
|
||||
}
|
||||
nrf52Bluetooth->clearBonds();
|
||||
}
|
||||
|
||||
void enterDfuMode()
|
||||
{
|
||||
void enterDfuMode() {
|
||||
// SDK kit does not have native USB like almost all other NRF52 boards
|
||||
#ifdef NRF_USE_SERIAL_DFU
|
||||
enterSerialDfu();
|
||||
enterSerialDfu();
|
||||
#else
|
||||
enterUf2Dfu();
|
||||
enterUf2Dfu();
|
||||
#endif
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user