mirror of
https://github.com/meshtastic/firmware.git
synced 2026-06-14 06:16:23 +00:00
Compare commits
110 Commits
itu_70cm
...
crowpanel-p4
| Author | SHA1 | Date | |
|---|---|---|---|
| abdb2e07c7 | |||
| 30f9c8cf2e | |||
| dc50b7fbf5 | |||
| 782356b325 | |||
| 94619389bb | |||
| 0fdfcfce0a | |||
| 948c867db5 | |||
| 4bc1078cfb | |||
| 9af3d74023 | |||
| 23065a145e | |||
| 05e6a7e5eb | |||
| c360815c0e | |||
| f0a30f72ff | |||
| 20eaefe706 | |||
| 9a63eb729d | |||
| 8b8d8938b9 | |||
| 0bb89644f9 | |||
| aef13bef89 | |||
| 6d31915e57 | |||
| aa89faa619 | |||
| ee796c75c9 | |||
| 918577b5b5 | |||
| c5a477f4c1 | |||
| fc79cf3971 | |||
| 0004616878 | |||
| a0a7209229 | |||
| 16f2ff8c9e | |||
| 894a2f43bd | |||
| e470723811 | |||
| 13ee648e3d | |||
| bec00b0a57 | |||
| 025630937c | |||
| 527e2dc7f7 | |||
| 095838f375 | |||
| e66fcde7e3 | |||
| dc478d2163 | |||
| f85ed3e59e | |||
| acb7ac6e3d | |||
| 91314c40f5 | |||
| c6282d3a3c | |||
| 3f35f2c6d7 | |||
| 407ab7abda | |||
| 6847f232a7 | |||
| e9cd77e758 | |||
| f586824435 | |||
| b0d7440bad | |||
| 8dc82c45b8 | |||
| 96538b492a | |||
| 023461cd92 | |||
| 41df7ef0bf | |||
| e49dab1f08 | |||
| 74f7d9ce3c | |||
| 6c74052ec3 | |||
| fb7d34afb9 | |||
| 814773f50e | |||
| 621aeb29b5 | |||
| 6f1d611c34 | |||
| fc871f42e4 | |||
| 9da0ee9c51 | |||
| 76734f0067 | |||
| fd65de142b | |||
| 5ae50b607d | |||
| 20fb1b2fc2 | |||
| a30ee4748a | |||
| 7726863043 | |||
| 61bab08d9e | |||
| da2cd98d98 | |||
| 979a1a1554 | |||
| a9a655c8f3 | |||
| e85ebc6859 | |||
| c9fc1edb4f | |||
| 483bb33749 | |||
| 62f62b4e8c | |||
| ad568021ba | |||
| ff8102e753 | |||
| 3c7d5435fe | |||
| dd1184f7af | |||
| 46c1230c39 | |||
| fa8f0f0dd8 | |||
| e22a22b325 | |||
| 35e4a877ee | |||
| a6f6175535 | |||
| 3e5f95f8ed | |||
| ca15aa86f2 | |||
| a3dcad08d4 | |||
| 70caa76fa0 | |||
| edb975d886 | |||
| 8d649e9a0b | |||
| 5c1e5f3dd6 | |||
| f1ffd05a55 | |||
| 6bde1d5fbb | |||
| bc311616a3 | |||
| 9fdeec173e | |||
| b24a6676a6 | |||
| a3f39de4b9 | |||
| d235d3f933 | |||
| 67726f9e3a | |||
| 3cbc9c91cb | |||
| 5c2afbf8ce | |||
| f1ca363efe | |||
| 814dc2db1b | |||
| fbeabe29ed | |||
| 7235afec2f | |||
| ef7036e9ed | |||
| c997e3bb65 | |||
| 028f781ea5 | |||
| 86cdff463b | |||
| 778090a269 | |||
| 14e9cb0fc3 | |||
| aa506ce4ab |
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"build": {
|
||||
"core": "esp32",
|
||||
"extra_flags": ["-DBOARD_HAS_PSRAM"],
|
||||
"f_cpu": "360000000L",
|
||||
"f_flash": "80000000L",
|
||||
"f_psram": "200000000L",
|
||||
"flash_mode": "qio",
|
||||
"hwids": [["0x1A86", "0x7522"]],
|
||||
"mcu": "esp32p4",
|
||||
"chip_variant": "esp32p4_es",
|
||||
"variant": "esp32p4"
|
||||
},
|
||||
"arduino": {
|
||||
"partitions": "default_16MB.csv"
|
||||
},
|
||||
"connectivity": ["bluetooth", "openthread"],
|
||||
"debug": {
|
||||
"openocd_target": "esp32p4.cfg"
|
||||
},
|
||||
"frameworks": ["arduino", "espidf"],
|
||||
"name": "CrowPanel Advanced ESP32-P4 HMI AI Display",
|
||||
"upload": {
|
||||
"flash_size": "16MB",
|
||||
"maximum_ram_size": 512000,
|
||||
"maximum_size": 16777216,
|
||||
"require_upload_port": true,
|
||||
"speed": 1500000
|
||||
},
|
||||
"url": "https://www.elecrow.com/crowpanel-advanced-5inch-esp32-p4-hmi-ai-display-800x480-ips-touch-screen-with-wifi-6.html",
|
||||
"vendor": "Elecrow"
|
||||
}
|
||||
+25
-1
@@ -14,4 +14,28 @@ const uint8_t FROMNUM_UUID_16[16u] = {0x53, 0x44, 0xe3, 0x47, 0x75, 0xaa, 0x70,
|
||||
const uint8_t LEGACY_LOGRADIO_UUID_16[16u] = {0xe2, 0xf2, 0x1e, 0xbe, 0xc5, 0x15, 0xcf, 0xaa,
|
||||
0x6b, 0x43, 0xfa, 0x78, 0x38, 0xd2, 0x6f, 0x6c};
|
||||
const uint8_t LOGRADIO_UUID_16[16u] = {0x47, 0x95, 0xDF, 0x8C, 0xDE, 0xE9, 0x44, 0x99,
|
||||
0x23, 0x44, 0xE6, 0x06, 0x49, 0x6E, 0x3D, 0x5A};
|
||||
0x23, 0x44, 0xE6, 0x06, 0x49, 0x6E, 0x3D, 0x5A};
|
||||
|
||||
void BluetoothApi::setup() {}
|
||||
void BluetoothApi::shutdown() {}
|
||||
void BluetoothApi::deinit() {}
|
||||
void BluetoothApi::clearBonds() {}
|
||||
bool BluetoothApi::isActive()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
bool BluetoothApi::isConnected()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
void BluetoothApi::sendLog(const uint8_t *logMessage, size_t length)
|
||||
{
|
||||
(void)logMessage;
|
||||
(void)length;
|
||||
}
|
||||
|
||||
void updateBatteryLevel(uint8_t level) __attribute__((weak));
|
||||
void updateBatteryLevel(uint8_t level)
|
||||
{
|
||||
(void)level;
|
||||
}
|
||||
@@ -24,9 +24,14 @@ void updateBatteryLevel(uint8_t level);
|
||||
class BluetoothApi
|
||||
{
|
||||
public:
|
||||
virtual ~BluetoothApi() = default;
|
||||
|
||||
virtual void setup();
|
||||
virtual void shutdown();
|
||||
virtual void deinit();
|
||||
virtual void clearBonds();
|
||||
virtual bool isActive();
|
||||
virtual bool isConnected();
|
||||
virtual int getRssi() = 0;
|
||||
virtual void sendLog(const uint8_t *logMessage, size_t length);
|
||||
};
|
||||
@@ -222,7 +222,7 @@ void RedirectablePrint::log_to_ble(const char *logLevel, const char *format, va_
|
||||
if (config.security.debug_log_api_enabled && !pauseBluetoothLogging) {
|
||||
bool isBleConnected = false;
|
||||
#ifdef ARCH_ESP32
|
||||
isBleConnected = nimbleBluetooth && nimbleBluetooth->isActive() && nimbleBluetooth->isConnected();
|
||||
isBleConnected = bluetoothApi && bluetoothApi->isActive() && bluetoothApi->isConnected();
|
||||
#elif defined(ARCH_NRF52)
|
||||
isBleConnected = nrf52Bluetooth != nullptr && nrf52Bluetooth->isConnected();
|
||||
#elif defined(ARCH_NRF54L15)
|
||||
@@ -240,7 +240,7 @@ void RedirectablePrint::log_to_ble(const char *logLevel, const char *format, va_
|
||||
auto buffer = std::unique_ptr<uint8_t[]>(new uint8_t[meshtastic_LogRecord_size]);
|
||||
size_t size = pb_encode_to_bytes(buffer.get(), meshtastic_LogRecord_size, meshtastic_LogRecord_fields, &logRecord);
|
||||
#ifdef ARCH_ESP32
|
||||
nimbleBluetooth->sendLog(buffer.get(), size);
|
||||
bluetoothApi->sendLog(buffer.get(), size);
|
||||
#elif defined(ARCH_NRF52)
|
||||
nrf52Bluetooth->sendLog(buffer.get(), size);
|
||||
#elif defined(ARCH_NRF54L15)
|
||||
|
||||
@@ -0,0 +1,762 @@
|
||||
#include "configuration.h"
|
||||
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32P4) && defined(CONFIG_ESP_HOSTED_ENABLED) && !MESHTASTIC_EXCLUDE_BLUETOOTH
|
||||
#include "BluetoothStatus.h"
|
||||
#include "PowerFSM.h"
|
||||
#include "bluetooth/HostedBluetooth.h"
|
||||
#include "concurrency/OSThread.h"
|
||||
#include "esp32-hal-hosted.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_event.h"
|
||||
#include "esp_hosted.h"
|
||||
#include "esp_hosted_event.h"
|
||||
#include "main.h"
|
||||
#include "mesh/PhoneAPI.h"
|
||||
#include "mesh/mesh-pb-constants.h"
|
||||
#include <BLEAdvertising.h>
|
||||
#include <BLEDevice.h>
|
||||
#include <BLESecurity.h>
|
||||
#include <BLEServer.h>
|
||||
#include <BLEUtils.h>
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <climits>
|
||||
#include <cstring>
|
||||
#include <driver/gpio.h>
|
||||
#include <mutex>
|
||||
|
||||
#include "host/ble_gap.h"
|
||||
#include "host/ble_store.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
/*
|
||||
* Maintainer note: HostedBluetooth intentionally stays close to NimbleBluetooth
|
||||
* but is not a strict drop-in equivalent.
|
||||
*
|
||||
* Intentional differences from NimbleBluetooth include:
|
||||
* - ESP-Hosted transport lifecycle handling (event callbacks + CP reset GPIO control).
|
||||
* - Data-length update behavior (Hosted currently requests ble_gap_set_data_len on connect).
|
||||
* - No Battery Service exposure here.
|
||||
*
|
||||
* If you modify common BLE flow in one class, review and likely mirror in both:
|
||||
* - PhoneAPI queueing/synchronization between BLE callbacks and runOnce().
|
||||
* - Security/pairing config and passkey UX/status updates.
|
||||
* - Mesh GATT characteristics, permissions, and advertising setup.
|
||||
* - Connection parameter tuning and reconnect/disconnect cleanup paths.
|
||||
*/
|
||||
constexpr uint16_t kPreferredBleMtu = 517;
|
||||
constexpr uint16_t kPreferredBleTxOctets = 251;
|
||||
constexpr uint16_t kPreferredBleTxTimeUs = (kPreferredBleTxOctets + 14) * 8;
|
||||
constexpr size_t kBluetoothToPhoneQueueSize = 3;
|
||||
constexpr size_t kBluetoothFromPhoneQueueSize = 3;
|
||||
|
||||
BLECharacteristic *fromNumCharacteristic = nullptr;
|
||||
BLECharacteristic *logRadioCharacteristic = nullptr;
|
||||
BLEServer *bleServer = nullptr;
|
||||
|
||||
static bool passkeyShowing = false;
|
||||
std::atomic<uint16_t> hostedConnHandle{BLE_HS_CONN_HANDLE_NONE};
|
||||
|
||||
void clearPairingDisplay()
|
||||
{
|
||||
if (!passkeyShowing) {
|
||||
return;
|
||||
}
|
||||
|
||||
passkeyShowing = false;
|
||||
#if HAS_SCREEN
|
||||
if (screen) {
|
||||
screen->endAlert();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
class HostedBluetoothPhoneAPI : public PhoneAPI, public concurrency::OSThread
|
||||
{
|
||||
public:
|
||||
HostedBluetoothPhoneAPI() : concurrency::OSThread("HostedBluetooth") { api_type = TYPE_BLE; }
|
||||
|
||||
std::mutex fromPhoneMutex;
|
||||
std::atomic<size_t> fromPhoneQueueSize{0};
|
||||
std::array<BLEValue, kBluetoothFromPhoneQueueSize> fromPhoneQueue{};
|
||||
|
||||
std::mutex toPhoneMutex;
|
||||
std::atomic<size_t> toPhoneQueueSize{0};
|
||||
std::array<std::array<uint8_t, meshtastic_FromRadio_size>, kBluetoothToPhoneQueueSize> toPhoneQueue{};
|
||||
std::array<size_t, kBluetoothToPhoneQueueSize> toPhoneQueueByteSizes{};
|
||||
std::atomic<bool> onReadCallbackIsWaitingForData{false};
|
||||
|
||||
protected:
|
||||
int32_t runOnce() override
|
||||
{
|
||||
while (fromPhoneQueueSize > 0 || onReadCallbackIsWaitingForData) {
|
||||
runOnceHandleFromPhoneQueue();
|
||||
runOnceHandleToPhoneQueue();
|
||||
}
|
||||
return INT32_MAX;
|
||||
}
|
||||
|
||||
void onNowHasData(uint32_t fromRadioNum) override
|
||||
{
|
||||
PhoneAPI::onNowHasData(fromRadioNum);
|
||||
|
||||
if (!fromNumCharacteristic || !bleServer || bleServer->getConnectedCount() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t val[4];
|
||||
put_le32(val, fromRadioNum);
|
||||
fromNumCharacteristic->setValue(val, sizeof(val));
|
||||
fromNumCharacteristic->notify();
|
||||
}
|
||||
|
||||
bool checkIsConnected() override { return bleServer && bleServer->getConnectedCount() > 0; }
|
||||
|
||||
private:
|
||||
void runOnceHandleToPhoneQueue()
|
||||
{
|
||||
if (!onReadCallbackIsWaitingForData) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t fromRadioBytes[meshtastic_FromRadio_size] = {0};
|
||||
size_t numBytes = getFromRadio(fromRadioBytes);
|
||||
|
||||
if (numBytes > 0 && toPhoneQueueSize < kBluetoothToPhoneQueueSize) {
|
||||
std::lock_guard<std::mutex> guard(toPhoneMutex);
|
||||
const size_t storeAtIndex = toPhoneQueueSize.load();
|
||||
memcpy(toPhoneQueue[storeAtIndex].data(), fromRadioBytes, numBytes);
|
||||
toPhoneQueueByteSizes[storeAtIndex] = numBytes;
|
||||
toPhoneQueueSize++;
|
||||
}
|
||||
|
||||
onReadCallbackIsWaitingForData = false;
|
||||
}
|
||||
|
||||
void runOnceHandleFromPhoneQueue()
|
||||
{
|
||||
if (fromPhoneQueueSize == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
BLEValue val;
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(fromPhoneMutex);
|
||||
val = fromPhoneQueue[0];
|
||||
for (size_t i = 1; i < fromPhoneQueueSize; ++i) {
|
||||
fromPhoneQueue[i - 1] = fromPhoneQueue[i];
|
||||
}
|
||||
fromPhoneQueueSize--;
|
||||
}
|
||||
|
||||
handleToRadio(val.getData(), val.getLength());
|
||||
}
|
||||
};
|
||||
|
||||
static HostedBluetoothPhoneAPI *bluetoothPhoneAPI = nullptr;
|
||||
uint8_t lastToRadio[MAX_TO_FROM_RADIO_SIZE] = {0};
|
||||
|
||||
class HostedBluetoothToRadioCallback : public BLECharacteristicCallbacks
|
||||
{
|
||||
public:
|
||||
void onWrite(BLECharacteristic *pCharacteristic) override
|
||||
{
|
||||
if (!bluetoothPhoneAPI) {
|
||||
return;
|
||||
}
|
||||
|
||||
BLEValue val;
|
||||
val.setValue(pCharacteristic->getData(), pCharacteristic->getLength());
|
||||
|
||||
if (memcmp(lastToRadio, val.getData(), val.getLength()) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (bluetoothPhoneAPI->fromPhoneQueueSize >= kBluetoothFromPhoneQueueSize) {
|
||||
LOG_WARN("Hosted BLE onWrite drop: queue full (%u bytes)", val.getLength());
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(lastToRadio, val.getData(), val.getLength());
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(bluetoothPhoneAPI->fromPhoneMutex);
|
||||
bluetoothPhoneAPI->fromPhoneQueue.at(bluetoothPhoneAPI->fromPhoneQueueSize) = val;
|
||||
bluetoothPhoneAPI->fromPhoneQueueSize++;
|
||||
}
|
||||
|
||||
bluetoothPhoneAPI->setIntervalFromNow(0);
|
||||
concurrency::mainDelay.interrupt();
|
||||
}
|
||||
};
|
||||
|
||||
class HostedBluetoothFromRadioCallback : public BLECharacteristicCallbacks
|
||||
{
|
||||
public:
|
||||
void onRead(BLECharacteristic *pCharacteristic) override
|
||||
{
|
||||
if (!bluetoothPhoneAPI) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (bluetoothPhoneAPI->toPhoneQueueSize == 0) {
|
||||
bluetoothPhoneAPI->onReadCallbackIsWaitingForData = true;
|
||||
bluetoothPhoneAPI->setIntervalFromNow(0);
|
||||
concurrency::mainDelay.interrupt();
|
||||
|
||||
int tries = 0;
|
||||
while (bluetoothPhoneAPI->onReadCallbackIsWaitingForData && tries < 4000) {
|
||||
delay(tries < 20 ? 1 : 5);
|
||||
tries++;
|
||||
if (tries == 4000) {
|
||||
LOG_WARN("BLE onRead: timeout waiting for data after %d tries, giving up and returning 0-size response",
|
||||
tries);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t fromRadioBytes[meshtastic_FromRadio_size] = {0};
|
||||
size_t numBytes = 0;
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(bluetoothPhoneAPI->toPhoneMutex);
|
||||
size_t pending = bluetoothPhoneAPI->toPhoneQueueSize.load();
|
||||
if (pending > 0) {
|
||||
numBytes = bluetoothPhoneAPI->toPhoneQueueByteSizes[0];
|
||||
memcpy(fromRadioBytes, bluetoothPhoneAPI->toPhoneQueue[0].data(), numBytes);
|
||||
|
||||
for (size_t i = 1; i < pending; ++i) {
|
||||
memcpy(bluetoothPhoneAPI->toPhoneQueue[i - 1].data(), bluetoothPhoneAPI->toPhoneQueue[i].data(),
|
||||
bluetoothPhoneAPI->toPhoneQueueByteSizes[i]);
|
||||
bluetoothPhoneAPI->toPhoneQueueByteSizes[i - 1] = bluetoothPhoneAPI->toPhoneQueueByteSizes[i];
|
||||
}
|
||||
|
||||
bluetoothPhoneAPI->toPhoneQueueSize--;
|
||||
}
|
||||
}
|
||||
|
||||
pCharacteristic->setValue(fromRadioBytes, numBytes);
|
||||
}
|
||||
};
|
||||
|
||||
class HostedBluetoothServerCallback : public BLEServerCallbacks
|
||||
{
|
||||
public:
|
||||
explicit HostedBluetoothServerCallback(HostedBluetooth *owner) : owner(owner) {}
|
||||
|
||||
private:
|
||||
HostedBluetooth *owner;
|
||||
|
||||
void onConnect(BLEServer *pServer, struct ble_gap_conn_desc *desc) override
|
||||
{
|
||||
BLEAddress peerAddr(desc->peer_id_addr);
|
||||
LOG_INFO("Hosted BLE incoming connection %s", peerAddr.toString().c_str());
|
||||
owner->setConnected(true);
|
||||
hostedConnHandle = desc->conn_handle;
|
||||
|
||||
const int dataLenResult = ble_gap_set_data_len(desc->conn_handle, kPreferredBleTxOctets, kPreferredBleTxTimeUs);
|
||||
if (dataLenResult != 0) {
|
||||
LOG_WARN("Hosted BLE failed to raise data length rc=%d", dataLenResult);
|
||||
}
|
||||
|
||||
pServer->updateConnParams(desc->conn_handle, 6, 12, 0, 200);
|
||||
}
|
||||
|
||||
void onDisconnect(BLEServer *pServer, struct ble_gap_conn_desc *desc) override
|
||||
{
|
||||
(void)pServer;
|
||||
(void)desc;
|
||||
LOG_INFO("Hosted BLE disconnected");
|
||||
owner->setConnected(false);
|
||||
meshtastic::BluetoothStatus newStatus(meshtastic::BluetoothStatus::ConnectionState::DISCONNECTED);
|
||||
bluetoothStatus->updateStatus(&newStatus);
|
||||
clearPairingDisplay();
|
||||
hostedConnHandle = BLE_HS_CONN_HANDLE_NONE;
|
||||
memset(lastToRadio, 0, sizeof(lastToRadio));
|
||||
|
||||
if (bluetoothPhoneAPI) {
|
||||
bluetoothPhoneAPI->close();
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(bluetoothPhoneAPI->fromPhoneMutex);
|
||||
bluetoothPhoneAPI->fromPhoneQueueSize = 0;
|
||||
}
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(bluetoothPhoneAPI->toPhoneMutex);
|
||||
bluetoothPhoneAPI->toPhoneQueueSize = 0;
|
||||
}
|
||||
bluetoothPhoneAPI->onReadCallbackIsWaitingForData = false;
|
||||
}
|
||||
|
||||
owner->startAdvertising();
|
||||
}
|
||||
};
|
||||
|
||||
class HostedBluetoothSecurityCallback : public BLESecurityCallbacks
|
||||
{
|
||||
public:
|
||||
void onPassKeyNotify(uint32_t passkey) override
|
||||
{
|
||||
LOG_INFO("*** Enter passkey %06u on the peer side ***", passkey);
|
||||
powerFSM.trigger(EVENT_BLUETOOTH_PAIR);
|
||||
|
||||
meshtastic::BluetoothStatus newStatus(std::to_string(passkey));
|
||||
bluetoothStatus->updateStatus(&newStatus);
|
||||
|
||||
#if HAS_SCREEN
|
||||
if (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 : 12;
|
||||
display->setTextAlignment(TEXT_ALIGN_CENTER);
|
||||
display->setFont(FONT_MEDIUM);
|
||||
display->drawString(x_offset + x, y_offset + y, "Bluetooth");
|
||||
#if !defined(M5STACK_UNITC6L)
|
||||
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");
|
||||
#endif
|
||||
display->setFont(FONT_LARGE);
|
||||
char pin[8];
|
||||
snprintf(pin, sizeof(pin), "%.3s %.3s", btPIN, btPIN + 3);
|
||||
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);
|
||||
char deviceName[64];
|
||||
snprintf(deviceName, sizeof(deviceName), "Name: %s", 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;
|
||||
}
|
||||
|
||||
void onAuthenticationComplete(ble_gap_conn_desc *desc) override
|
||||
{
|
||||
(void)desc;
|
||||
LOG_INFO("Hosted BLE authentication complete");
|
||||
|
||||
meshtastic::BluetoothStatus newStatus(meshtastic::BluetoothStatus::ConnectionState::CONNECTED);
|
||||
bluetoothStatus->updateStatus(&newStatus);
|
||||
clearPairingDisplay();
|
||||
}
|
||||
};
|
||||
|
||||
HostedBluetoothToRadioCallback *toRadioCallbacks = nullptr;
|
||||
HostedBluetoothFromRadioCallback *fromRadioCallbacks = nullptr;
|
||||
HostedBluetoothSecurityCallback *securityCallbacks = nullptr;
|
||||
|
||||
gpio_num_t getSlaveResetGpio()
|
||||
{
|
||||
#if defined(CONFIG_ESP_HOSTED_GPIO_SLAVE_RESET_SLAVE)
|
||||
return static_cast<gpio_num_t>(CONFIG_ESP_HOSTED_GPIO_SLAVE_RESET_SLAVE);
|
||||
#elif defined(CONFIG_ESP_HOSTED_SDIO_GPIO_RESET_SLAVE)
|
||||
return static_cast<gpio_num_t>(CONFIG_ESP_HOSTED_SDIO_GPIO_RESET_SLAVE);
|
||||
#else
|
||||
return GPIO_NUM_NC;
|
||||
#endif
|
||||
}
|
||||
|
||||
void setSlaveResetLine(bool assertReset)
|
||||
{
|
||||
const gpio_num_t gpioNum = getSlaveResetGpio();
|
||||
if (gpioNum == GPIO_NUM_NC || gpioNum < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (gpio_reset_pin(gpioNum) != ESP_OK) {
|
||||
return;
|
||||
}
|
||||
if (gpio_set_direction(gpioNum, GPIO_MODE_OUTPUT) != ESP_OK) {
|
||||
return;
|
||||
}
|
||||
|
||||
const int activeLevel =
|
||||
#if defined(CONFIG_ESP_HOSTED_SDIO_RESET_ACTIVE_HIGH)
|
||||
1;
|
||||
#else
|
||||
0;
|
||||
#endif
|
||||
const int inactiveLevel = activeLevel ? 0 : 1;
|
||||
|
||||
gpio_set_level(gpioNum, assertReset ? activeLevel : inactiveLevel);
|
||||
LOG_DEBUG("[HostedBluetooth] setSlaveResetLine: GPIO[%d] -> %d (assertReset=%d, activeLevel=%d)", gpioNum,
|
||||
assertReset ? activeLevel : inactiveLevel, assertReset, activeLevel);
|
||||
}
|
||||
|
||||
void hostedEventHandler(void *arg, esp_event_base_t eventBase, int32_t eventId, void *eventData)
|
||||
{
|
||||
(void)eventData;
|
||||
|
||||
auto *self = static_cast<HostedBluetooth *>(arg);
|
||||
if (!self || eventBase != ESP_HOSTED_EVENT) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (eventId) {
|
||||
case ESP_HOSTED_EVENT_TRANSPORT_UP:
|
||||
LOG_INFO("ESP-Hosted transport is up");
|
||||
self->setConnected(true);
|
||||
break;
|
||||
case ESP_HOSTED_EVENT_TRANSPORT_DOWN:
|
||||
LOG_WARN("ESP-Hosted transport is down");
|
||||
[[fallthrough]];
|
||||
case ESP_HOSTED_EVENT_TRANSPORT_FAILURE:
|
||||
if (eventId == ESP_HOSTED_EVENT_TRANSPORT_FAILURE) {
|
||||
LOG_ERROR("ESP-Hosted transport failure");
|
||||
}
|
||||
self->setConnected(false);
|
||||
break;
|
||||
case ESP_HOSTED_EVENT_CP_INIT:
|
||||
case ESP_HOSTED_EVENT_CP_HEARTBEAT:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
HostedBluetooth::HostedBluetooth() {}
|
||||
|
||||
HostedBluetooth::~HostedBluetooth()
|
||||
{
|
||||
deinit();
|
||||
}
|
||||
|
||||
bool HostedBluetooth::registerCallbacks()
|
||||
{
|
||||
if (callbacksRegistered) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Defensive cleanup in case setup() is called again after an incomplete/early previous init.
|
||||
// esp_event_handler_unregister can safely fail if the handler wasn't present.
|
||||
esp_event_handler_unregister(ESP_HOSTED_EVENT, ESP_EVENT_ANY_ID, hostedEventHandler);
|
||||
|
||||
esp_err_t err = esp_event_handler_register(ESP_HOSTED_EVENT, ESP_EVENT_ANY_ID, hostedEventHandler, this);
|
||||
if (err != ESP_OK && err != ESP_ERR_INVALID_STATE) {
|
||||
LOG_ERROR("Failed to register hosted event handler: %s", esp_err_to_name(err));
|
||||
return false;
|
||||
}
|
||||
if (err == ESP_ERR_INVALID_STATE) {
|
||||
LOG_WARN("Hosted event handler already registered, continuing");
|
||||
}
|
||||
|
||||
callbacksRegistered = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void HostedBluetooth::unregisterCallbacks()
|
||||
{
|
||||
if (!callbacksRegistered) {
|
||||
return;
|
||||
}
|
||||
|
||||
esp_event_handler_unregister(ESP_HOSTED_EVENT, ESP_EVENT_ANY_ID, hostedEventHandler);
|
||||
|
||||
callbacksRegistered = false;
|
||||
}
|
||||
|
||||
void HostedBluetooth::setup()
|
||||
{
|
||||
if (active) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Ensure co-processor is released from reset before bringing transport back up.
|
||||
setSlaveResetLine(false);
|
||||
LOG_DEBUG("[HostedBluetooth] setup(): Released co-processor from reset");
|
||||
|
||||
if (!registerCallbacks()) {
|
||||
return;
|
||||
}
|
||||
|
||||
active = setupGatt();
|
||||
firstRssiLogged.store(false);
|
||||
if (active) {
|
||||
LOG_INFO("ESP-Hosted Bluetooth ready");
|
||||
} else {
|
||||
LOG_ERROR("Hosted BLE setup failed in hosted mode");
|
||||
deinit();
|
||||
}
|
||||
}
|
||||
|
||||
void HostedBluetooth::shutdown()
|
||||
{
|
||||
deinit();
|
||||
}
|
||||
|
||||
void HostedBluetooth::deinit()
|
||||
{
|
||||
if (!active && !callbacksRegistered) {
|
||||
return;
|
||||
}
|
||||
|
||||
shutdownGatt();
|
||||
if (BLEDevice::getInitialized()) {
|
||||
BLEDevice::deinit(false);
|
||||
} else {
|
||||
hostedDeinitBLE();
|
||||
}
|
||||
|
||||
// Hold co-processor in reset when hosted transport is disabled to reduce idle draw.
|
||||
setSlaveResetLine(true);
|
||||
|
||||
unregisterCallbacks();
|
||||
|
||||
connected.store(false);
|
||||
active = false;
|
||||
rssi.store(0);
|
||||
firstRssiLogged.store(false);
|
||||
}
|
||||
|
||||
void HostedBluetooth::clearBonds()
|
||||
{
|
||||
ble_store_util_delete_all(BLE_STORE_OBJ_TYPE_PEER_SEC, nullptr);
|
||||
ble_store_util_delete_all(BLE_STORE_OBJ_TYPE_CCCD, nullptr);
|
||||
}
|
||||
|
||||
bool HostedBluetooth::isActive()
|
||||
{
|
||||
return active;
|
||||
}
|
||||
|
||||
bool HostedBluetooth::isConnected()
|
||||
{
|
||||
if (!connected.load()) {
|
||||
return false;
|
||||
}
|
||||
return bleServer && bleServer->getConnectedCount() > 0;
|
||||
}
|
||||
|
||||
int HostedBluetooth::getRssi()
|
||||
{
|
||||
if (!bleServer || !isConnected()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint16_t connHandle = hostedConnHandle.load();
|
||||
if (connHandle == BLE_HS_CONN_HANDLE_NONE) {
|
||||
const auto peers = bleServer->getPeerDevices(true);
|
||||
if (!peers.empty()) {
|
||||
connHandle = peers.begin()->first;
|
||||
hostedConnHandle = connHandle;
|
||||
}
|
||||
}
|
||||
if (connHandle == BLE_HS_CONN_HANDLE_NONE) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int8_t currentRssi = 0;
|
||||
const int rc = ble_gap_conn_rssi(connHandle, ¤tRssi);
|
||||
if (rc == 0) {
|
||||
setRssi(currentRssi);
|
||||
return currentRssi;
|
||||
}
|
||||
|
||||
return rssi.load();
|
||||
}
|
||||
|
||||
void HostedBluetooth::sendLog(const uint8_t *logMessage, size_t length)
|
||||
{
|
||||
if (!logRadioCharacteristic || !isConnected() || length > 512) {
|
||||
return;
|
||||
}
|
||||
|
||||
logRadioCharacteristic->setValue(logMessage, length);
|
||||
logRadioCharacteristic->notify();
|
||||
}
|
||||
|
||||
void HostedBluetooth::setConnected(bool value)
|
||||
{
|
||||
connected.store(value);
|
||||
}
|
||||
|
||||
void HostedBluetooth::setRssi(int value)
|
||||
{
|
||||
rssi.store(value);
|
||||
maybeLogFirstRssi(value);
|
||||
}
|
||||
|
||||
void HostedBluetooth::maybeLogFirstRssi(int value)
|
||||
{
|
||||
bool expected = false;
|
||||
if (firstRssiLogged.compare_exchange_strong(expected, true)) {
|
||||
LOG_INFO("ESP-Hosted first RSSI update: %d dBm", value);
|
||||
}
|
||||
}
|
||||
|
||||
bool HostedBluetooth::setupGatt()
|
||||
{
|
||||
memset(lastToRadio, 0, sizeof(lastToRadio));
|
||||
hostedConnHandle = BLE_HS_CONN_HANDLE_NONE;
|
||||
|
||||
if (!BLEDevice::init(getDeviceName())) {
|
||||
LOG_ERROR("Hosted BLE init failed");
|
||||
return false;
|
||||
}
|
||||
#ifdef ESP_PWR_LVL_P9
|
||||
BLEDevice::setPower(ESP_PWR_LVL_P9);
|
||||
#endif
|
||||
|
||||
BLESecurity *security = new BLESecurity();
|
||||
security->setInitEncryptionKey(ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK);
|
||||
security->setRespEncryptionKey(ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK);
|
||||
if (config.bluetooth.mode != meshtastic_Config_BluetoothConfig_PairingMode_NO_PIN) {
|
||||
security->setCapability(ESP_IO_CAP_OUT);
|
||||
if (config.bluetooth.mode == meshtastic_Config_BluetoothConfig_PairingMode_RANDOM_PIN) {
|
||||
LOG_INFO("Hosted BLE using random passkey");
|
||||
security->setPassKey(false);
|
||||
} else {
|
||||
LOG_INFO("Hosted BLE using fixed passkey");
|
||||
security->setPassKey(true, config.bluetooth.fixed_pin);
|
||||
}
|
||||
// Enable authorization requirements:
|
||||
// - bonding: true (for persistent storage of the keys)
|
||||
// - MITM: true (enables Man-In-The-Middle protection for password prompts)
|
||||
// - secure connection: true (enables secure connection for encryption)
|
||||
security->setAuthenticationMode(true, true, true);
|
||||
} else {
|
||||
security->setCapability(ESP_IO_CAP_NONE);
|
||||
security->setAuthenticationMode(true, false, false);
|
||||
}
|
||||
|
||||
if (!securityCallbacks) {
|
||||
securityCallbacks = new HostedBluetoothSecurityCallback();
|
||||
}
|
||||
BLEDevice::setSecurityCallbacks(securityCallbacks);
|
||||
|
||||
const int mtuResult = BLEDevice::setMTU(kPreferredBleMtu);
|
||||
if (mtuResult == 0) {
|
||||
LOG_INFO("Hosted BLE MTU request set to %u", kPreferredBleMtu);
|
||||
} else {
|
||||
LOG_WARN("Hosted BLE unable to request MTU %u, rc=%d", kPreferredBleMtu, mtuResult);
|
||||
}
|
||||
|
||||
bleServer = BLEDevice::createServer();
|
||||
if (!bleServer) {
|
||||
LOG_ERROR("Hosted BLE createServer failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
const int nameRc = ble_svc_gap_device_name_set(BLEDevice::getDeviceName().c_str());
|
||||
if (nameRc != 0) {
|
||||
LOG_WARN("Hosted BLE device_name_set rc=%d %s", nameRc, BLEUtils::returnCodeToString(nameRc));
|
||||
}
|
||||
|
||||
bleServer->setCallbacks(new HostedBluetoothServerCallback(this));
|
||||
|
||||
BLEService *meshService = bleServer->createService(MESH_SERVICE_UUID);
|
||||
if (!meshService) {
|
||||
LOG_ERROR("Hosted BLE mesh service creation failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
BLECharacteristic *toRadioCharacteristic = nullptr;
|
||||
BLECharacteristic *fromRadioCharacteristic = nullptr;
|
||||
if (config.bluetooth.mode == meshtastic_Config_BluetoothConfig_PairingMode_NO_PIN) {
|
||||
toRadioCharacteristic = meshService->createCharacteristic(TORADIO_UUID, BLECharacteristic::PROPERTY_WRITE);
|
||||
fromRadioCharacteristic = meshService->createCharacteristic(FROMRADIO_UUID, BLECharacteristic::PROPERTY_READ);
|
||||
fromNumCharacteristic = meshService->createCharacteristic(FROMNUM_UUID, BLECharacteristic::PROPERTY_NOTIFY |
|
||||
BLECharacteristic::PROPERTY_READ);
|
||||
logRadioCharacteristic = meshService->createCharacteristic(LOGRADIO_UUID, BLECharacteristic::PROPERTY_NOTIFY |
|
||||
BLECharacteristic::PROPERTY_READ);
|
||||
} else {
|
||||
toRadioCharacteristic = meshService->createCharacteristic(TORADIO_UUID, BLECharacteristic::PROPERTY_WRITE |
|
||||
BLECharacteristic::PROPERTY_WRITE_AUTHEN |
|
||||
BLECharacteristic::PROPERTY_WRITE_ENC);
|
||||
fromRadioCharacteristic = meshService->createCharacteristic(FROMRADIO_UUID, BLECharacteristic::PROPERTY_READ |
|
||||
BLECharacteristic::PROPERTY_READ_AUTHEN |
|
||||
BLECharacteristic::PROPERTY_READ_ENC);
|
||||
fromNumCharacteristic = meshService->createCharacteristic(
|
||||
FROMNUM_UUID, BLECharacteristic::PROPERTY_NOTIFY | BLECharacteristic::PROPERTY_READ |
|
||||
BLECharacteristic::PROPERTY_READ_AUTHEN | BLECharacteristic::PROPERTY_READ_ENC);
|
||||
logRadioCharacteristic = meshService->createCharacteristic(
|
||||
LOGRADIO_UUID, BLECharacteristic::PROPERTY_NOTIFY | BLECharacteristic::PROPERTY_READ |
|
||||
BLECharacteristic::PROPERTY_READ_AUTHEN | BLECharacteristic::PROPERTY_READ_ENC);
|
||||
}
|
||||
|
||||
if (!toRadioCharacteristic || !fromRadioCharacteristic || !fromNumCharacteristic || !logRadioCharacteristic) {
|
||||
LOG_ERROR("Hosted BLE characteristic creation failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!bluetoothPhoneAPI) {
|
||||
bluetoothPhoneAPI = new HostedBluetoothPhoneAPI();
|
||||
}
|
||||
|
||||
if (!toRadioCallbacks) {
|
||||
toRadioCallbacks = new HostedBluetoothToRadioCallback();
|
||||
}
|
||||
if (!fromRadioCallbacks) {
|
||||
fromRadioCallbacks = new HostedBluetoothFromRadioCallback();
|
||||
}
|
||||
|
||||
toRadioCharacteristic->setCallbacks(toRadioCallbacks);
|
||||
fromRadioCharacteristic->setCallbacks(fromRadioCallbacks);
|
||||
meshService->start();
|
||||
|
||||
startAdvertising();
|
||||
return true;
|
||||
}
|
||||
|
||||
void HostedBluetooth::shutdownGatt()
|
||||
{
|
||||
if (bluetoothPhoneAPI) {
|
||||
bluetoothPhoneAPI->close();
|
||||
delete bluetoothPhoneAPI;
|
||||
bluetoothPhoneAPI = nullptr;
|
||||
}
|
||||
|
||||
delete toRadioCallbacks;
|
||||
toRadioCallbacks = nullptr;
|
||||
|
||||
delete fromRadioCallbacks;
|
||||
fromRadioCallbacks = nullptr;
|
||||
|
||||
delete securityCallbacks;
|
||||
securityCallbacks = nullptr;
|
||||
|
||||
if (bleServer) {
|
||||
BLEAdvertising *advertising = BLEDevice::getAdvertising();
|
||||
if (advertising) {
|
||||
advertising->stop();
|
||||
}
|
||||
}
|
||||
|
||||
fromNumCharacteristic = nullptr;
|
||||
logRadioCharacteristic = nullptr;
|
||||
bleServer = nullptr;
|
||||
hostedConnHandle = BLE_HS_CONN_HANDLE_NONE;
|
||||
}
|
||||
|
||||
void HostedBluetooth::startAdvertising()
|
||||
{
|
||||
BLEAdvertising *advertising = BLEDevice::getAdvertising();
|
||||
if (!advertising) {
|
||||
LOG_ERROR("Hosted BLE getAdvertising failed");
|
||||
return;
|
||||
}
|
||||
|
||||
advertising->stop();
|
||||
advertising->reset();
|
||||
advertising->addServiceUUID(MESH_SERVICE_UUID);
|
||||
|
||||
BLEAdvertisementData scan;
|
||||
scan.setName(getDeviceName());
|
||||
advertising->setScanResponseData(scan);
|
||||
advertising->setMinPreferred(0x06);
|
||||
advertising->setMaxPreferred(0x12);
|
||||
|
||||
if (!advertising->start(0)) {
|
||||
LOG_ERROR("Hosted BLE failed to start advertising");
|
||||
} else {
|
||||
LOG_INFO("Hosted BLE advertising started");
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,43 @@
|
||||
#pragma once
|
||||
|
||||
#include "BluetoothCommon.h"
|
||||
#include <atomic>
|
||||
|
||||
/**
|
||||
* Placeholder backend for ESP32-P4 + ESP-Hosted BLE transport.
|
||||
*
|
||||
* This intentionally mirrors the NimbleBluetooth surface used by the firmware,
|
||||
* so the caller side can stay stable while we implement esp-hosted internals.
|
||||
*/
|
||||
class HostedBluetooth : public BluetoothApi
|
||||
{
|
||||
public:
|
||||
HostedBluetooth();
|
||||
~HostedBluetooth() override;
|
||||
|
||||
void setup() override;
|
||||
void shutdown() override;
|
||||
void deinit() override;
|
||||
void clearBonds() override;
|
||||
bool isActive() override;
|
||||
bool isConnected() override;
|
||||
int getRssi() override;
|
||||
void sendLog(const uint8_t *logMessage, size_t length) override;
|
||||
void startAdvertising();
|
||||
|
||||
void setConnected(bool value);
|
||||
void setRssi(int value);
|
||||
|
||||
private:
|
||||
bool setupGatt();
|
||||
void shutdownGatt();
|
||||
void maybeLogFirstRssi(int value);
|
||||
bool registerCallbacks();
|
||||
void unregisterCallbacks();
|
||||
|
||||
bool active = false;
|
||||
std::atomic<bool> connected{false};
|
||||
std::atomic<int> rssi{0};
|
||||
std::atomic<bool> firstRssiLogged{false};
|
||||
bool callbacksRegistered = false;
|
||||
};
|
||||
+1
-2
@@ -52,8 +52,7 @@
|
||||
#include "mesh/http/WebServer.h"
|
||||
#endif
|
||||
#if !MESHTASTIC_EXCLUDE_BLUETOOTH
|
||||
#include "nimble/NimbleBluetooth.h"
|
||||
NimbleBluetooth *nimbleBluetooth = nullptr;
|
||||
BluetoothApi *bluetoothApi = nullptr;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
+2
-2
@@ -12,8 +12,8 @@
|
||||
#include <SPI.h>
|
||||
#include <map>
|
||||
#if defined(ARCH_ESP32) && !defined(CONFIG_IDF_TARGET_ESP32S2)
|
||||
#include "nimble/NimbleBluetooth.h"
|
||||
extern NimbleBluetooth *nimbleBluetooth;
|
||||
#include "BluetoothCommon.h"
|
||||
extern BluetoothApi *bluetoothApi;
|
||||
#endif
|
||||
#ifdef ARCH_NRF52
|
||||
#include "NRF52Bluetooth.h"
|
||||
|
||||
@@ -1338,9 +1338,9 @@ void AdminModule::handleGetDeviceConnectionStatus(const meshtastic_MeshPacket &r
|
||||
conn.has_bluetooth = true;
|
||||
conn.bluetooth.pin = config.bluetooth.fixed_pin;
|
||||
#ifdef ARCH_ESP32
|
||||
if (config.bluetooth.enabled && nimbleBluetooth) {
|
||||
conn.bluetooth.is_connected = nimbleBluetooth->isConnected();
|
||||
conn.bluetooth.rssi = nimbleBluetooth->getRssi();
|
||||
if (config.bluetooth.enabled && bluetoothApi) {
|
||||
conn.bluetooth.is_connected = bluetoothApi->isConnected();
|
||||
conn.bluetooth.rssi = bluetoothApi->getRssi();
|
||||
}
|
||||
#elif defined(ARCH_NRF52)
|
||||
if (config.bluetooth.enabled && nrf52Bluetooth) {
|
||||
@@ -1605,8 +1605,8 @@ void disableBluetooth()
|
||||
{
|
||||
#if HAS_BLUETOOTH
|
||||
#ifdef ARCH_ESP32
|
||||
if (nimbleBluetooth)
|
||||
nimbleBluetooth->deinit();
|
||||
if (bluetoothApi)
|
||||
bluetoothApi->deinit();
|
||||
#elif defined(ARCH_NRF52)
|
||||
if (nrf52Bluetooth)
|
||||
nrf52Bluetooth->shutdown();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#include "configuration.h"
|
||||
#if !MESHTASTIC_EXCLUDE_BLUETOOTH
|
||||
#if !MESHTASTIC_EXCLUDE_BLUETOOTH && !defined(CONFIG_IDF_TARGET_ESP32P4)
|
||||
#include "BluetoothCommon.h"
|
||||
#include "NimbleBluetooth.h"
|
||||
#include "PowerFSM.h"
|
||||
@@ -25,6 +25,9 @@
|
||||
|
||||
namespace
|
||||
{
|
||||
// Maintainer note: this backend intentionally diverges from HostedBluetooth in a few platform-specific areas.
|
||||
// If you change shared BLE flow here (PhoneAPI queue/sync, security/pairing, mesh GATT/advertising,
|
||||
// connect/disconnect handling), review and update HostedBluetooth.cpp as needed.
|
||||
constexpr uint16_t kPreferredBleMtu = 517;
|
||||
constexpr uint16_t kPreferredBleTxOctets = 251;
|
||||
constexpr uint16_t kPreferredBleTxTimeUs = (kPreferredBleTxOctets + 14) * 8;
|
||||
@@ -568,7 +571,7 @@ class NimbleBluetoothSecurityCallback : public BLESecurityCallbacks
|
||||
display->setTextAlignment(TEXT_ALIGN_CENTER);
|
||||
display->setFont(FONT_MEDIUM);
|
||||
display->drawString(x_offset + x, y_offset + y, "Bluetooth");
|
||||
#if !defined(OLED_TINY)
|
||||
#if !defined(OLED_TINY) && !defined(M5STACK_UNITC6L)
|
||||
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");
|
||||
@@ -739,6 +742,19 @@ int NimbleBluetooth::getRssi()
|
||||
return 0; // No active BLE connection
|
||||
}
|
||||
|
||||
uint16_t connHandle = nimbleBluetoothConnHandle.load();
|
||||
|
||||
if (connHandle == BLE_HS_CONN_HANDLE_NONE) {
|
||||
const auto peers = bleServer->getPeerDevices(true);
|
||||
if (!peers.empty()) {
|
||||
connHandle = peers.begin()->first;
|
||||
nimbleBluetoothConnHandle = connHandle;
|
||||
}
|
||||
}
|
||||
|
||||
if (connHandle == BLE_HS_CONN_HANDLE_NONE) {
|
||||
return 0; // Connection handle not available yet
|
||||
}
|
||||
int8_t rssi = 0;
|
||||
const int rc = ble_gap_conn_rssi(conn_handle, &rssi);
|
||||
|
||||
@@ -862,7 +878,7 @@ void NimbleBluetooth::setupService()
|
||||
/// Given a level between 0-100, update the BLE attribute
|
||||
void updateBatteryLevel(uint8_t level)
|
||||
{
|
||||
if ((config.bluetooth.enabled == true) && nimbleBluetooth && nimbleBluetooth->isConnected()) {
|
||||
if ((config.bluetooth.enabled == true) && BatteryCharacteristic && nimbleBluetooth && nimbleBluetooth->isConnected()) {
|
||||
BatteryCharacteristic->setValue(&level, 1);
|
||||
BatteryCharacteristic->notify();
|
||||
}
|
||||
@@ -892,4 +908,13 @@ void clearNVS()
|
||||
ESP.restart();
|
||||
#endif
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
void updateBatteryLevel(uint8_t level)
|
||||
{
|
||||
(void)level;
|
||||
}
|
||||
|
||||
void clearNVS() {}
|
||||
#endif
|
||||
|
||||
@@ -1,18 +1,19 @@
|
||||
#pragma once
|
||||
#include "BluetoothCommon.h"
|
||||
|
||||
class NimbleBluetooth : BluetoothApi
|
||||
class NimbleBluetooth : public BluetoothApi
|
||||
{
|
||||
public:
|
||||
void setup();
|
||||
void shutdown();
|
||||
void deinit();
|
||||
void clearBonds();
|
||||
bool isActive();
|
||||
bool isConnected();
|
||||
int getRssi();
|
||||
void sendLog(const uint8_t *logMessage, size_t length);
|
||||
void setup() override;
|
||||
void shutdown() override;
|
||||
void deinit() override;
|
||||
void clearBonds() override;
|
||||
bool isActive() override;
|
||||
bool isConnected() override;
|
||||
int getRssi() override;
|
||||
void sendLog(const uint8_t *logMessage, size_t length) override;
|
||||
void startAdvertising();
|
||||
virtual ~NimbleBluetooth() {}
|
||||
bool isDeInit = false;
|
||||
|
||||
private:
|
||||
|
||||
@@ -4,9 +4,13 @@
|
||||
#include "esp_task_wdt.h"
|
||||
#include "main.h"
|
||||
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !MESHTASTIC_EXCLUDE_BLUETOOTH
|
||||
#if !MESHTASTIC_EXCLUDE_BLUETOOTH
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32P4)
|
||||
#include "bluetooth/HostedBluetooth.h"
|
||||
#elif !defined(CONFIG_IDF_TARGET_ESP32S2)
|
||||
#include "nimble/NimbleBluetooth.h"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include <MeshtasticOTA.h>
|
||||
|
||||
@@ -40,16 +44,30 @@ void setBluetoothEnable(bool enable)
|
||||
if (config.bluetooth.enabled == true)
|
||||
#endif
|
||||
{
|
||||
if (!nimbleBluetooth) {
|
||||
nimbleBluetooth = new NimbleBluetooth();
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32P4)
|
||||
if (!enable) {
|
||||
if (bluetoothApi && bluetoothApi->isActive()) {
|
||||
bluetoothApi->shutdown();
|
||||
powerMon->clearState(meshtastic_PowerMon_State_BT_On);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (enable && !nimbleBluetooth->isActive()) {
|
||||
#endif
|
||||
|
||||
if (!bluetoothApi) {
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32P4)
|
||||
bluetoothApi = new HostedBluetooth();
|
||||
#else
|
||||
bluetoothApi = new NimbleBluetooth();
|
||||
#endif
|
||||
}
|
||||
if (enable && !bluetoothApi->isActive()) {
|
||||
powerMon->setState(meshtastic_PowerMon_State_BT_On);
|
||||
nimbleBluetooth->setup();
|
||||
bluetoothApi->setup();
|
||||
}
|
||||
// For ESP32, no way to recover from bluetooth shutdown without reboot
|
||||
// BLE advertising automatically stops when MCU enters light-sleep(?)
|
||||
// For deep-sleep, shutdown hardware with nimbleBluetooth->deinit(). Requires reboot to reverse
|
||||
// For deep-sleep, shutdown hardware with bluetoothApi->deinit(). Requires reboot to reverse
|
||||
}
|
||||
}
|
||||
#else
|
||||
|
||||
+2
-2
@@ -234,8 +234,8 @@ void doDeepSleep(uint32_t msecToWake, bool skipPreflight = false, bool skipSaveN
|
||||
|
||||
#if defined(ARCH_ESP32) && !MESHTASTIC_EXCLUDE_BLUETOOTH
|
||||
// Full shutdown of bluetooth hardware
|
||||
if (nimbleBluetooth)
|
||||
nimbleBluetooth->deinit();
|
||||
if (bluetoothApi)
|
||||
bluetoothApi->deinit();
|
||||
#endif
|
||||
|
||||
#ifdef ARCH_ESP32
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
#ifndef Pins_Arduino_h
|
||||
#define Pins_Arduino_h
|
||||
|
||||
#include "soc/soc_caps.h"
|
||||
#include <stdint.h>
|
||||
|
||||
// BOOT_MODE 35
|
||||
// BOOT_MODE2 36 pullup
|
||||
|
||||
static const uint8_t TX = 37;
|
||||
static const uint8_t RX = 38;
|
||||
|
||||
#if defined(CROWPANEL_ADV_P4_50)
|
||||
static const uint8_t SDA = 45;
|
||||
static const uint8_t SCL = 46;
|
||||
#else
|
||||
static const uint8_t SDA = 45;
|
||||
static const uint8_t SCL = 46;
|
||||
#endif
|
||||
|
||||
// Use GPIOs 36 or lower on the P4 DevKit to avoid LDO power issues with high numbered GPIOs.
|
||||
static const uint8_t SS = 30;
|
||||
static const uint8_t MOSI = 48;
|
||||
static const uint8_t MISO = 47;
|
||||
static const uint8_t SCK = 26;
|
||||
|
||||
static const uint8_t A0 = 16;
|
||||
static const uint8_t A1 = 17;
|
||||
static const uint8_t A2 = 18;
|
||||
static const uint8_t A3 = 19;
|
||||
static const uint8_t A4 = 20;
|
||||
static const uint8_t A5 = 21;
|
||||
static const uint8_t A6 = 22;
|
||||
static const uint8_t A7 = 23;
|
||||
static const uint8_t A8 = 49;
|
||||
static const uint8_t A9 = 50;
|
||||
static const uint8_t A10 = 51;
|
||||
static const uint8_t A11 = 52;
|
||||
static const uint8_t A12 = 53;
|
||||
static const uint8_t A13 = 54;
|
||||
|
||||
static const uint8_t T0 = 2;
|
||||
static const uint8_t T1 = 3;
|
||||
static const uint8_t T2 = 4;
|
||||
static const uint8_t T3 = 5;
|
||||
static const uint8_t T4 = 6;
|
||||
static const uint8_t T5 = 7;
|
||||
static const uint8_t T6 = 8;
|
||||
static const uint8_t T7 = 9;
|
||||
static const uint8_t T8 = 10;
|
||||
static const uint8_t T9 = 11;
|
||||
static const uint8_t T10 = 12;
|
||||
static const uint8_t T11 = 13;
|
||||
static const uint8_t T12 = 14;
|
||||
static const uint8_t T13 = 15;
|
||||
|
||||
/* ESP32-P4 EV Function board specific definitions */
|
||||
// ETH
|
||||
// #define ETH_PHY_TYPE ETH_PHY_TLK110
|
||||
// #define ETH_PHY_ADDR 1
|
||||
// #define ETH_PHY_MDC 31
|
||||
// #define ETH_PHY_MDIO 52
|
||||
// #define ETH_PHY_POWER 51
|
||||
// #define ETH_RMII_TX_EN 49
|
||||
// #define ETH_RMII_TX0 34
|
||||
// #define ETH_RMII_TX1 35
|
||||
// #define ETH_RMII_RX0 29
|
||||
// #define ETH_RMII_RX1_EN 30
|
||||
// #define ETH_RMII_CRS_DV 28
|
||||
// #define ETH_RMII_CLK 50
|
||||
// #define ETH_CLK_MODE EMAC_CLK_EXT_IN
|
||||
|
||||
// SDMMC
|
||||
#define BOARD_HAS_SDMMC
|
||||
#define BOARD_HAS_SD_SDMMC
|
||||
#define SDMMC_CMD 44 // SD_CMD/MOSI
|
||||
#define SDMMC_CLK 43 // SD_CLK
|
||||
#define SDMMC_D0 39 // SD_D0/MISO
|
||||
#define SD_CS -1 // No CS pin in SDMMC mode
|
||||
|
||||
#if defined(CROWPANEL_ADV_P4_50)
|
||||
#define BOARD_SDMMC_SLOT 1
|
||||
// Workaround for Arduino-ESP32 P4 SPI LDO auto-config on SDMMC slot0 pins (47/48).
|
||||
// Use a valid GPIO (aligned to variant LoRa CS) and pre-tag it in initVariant()
|
||||
// so setLDOPower() short-circuits.
|
||||
#define BOARD_SDMMC_POWER_PIN 30
|
||||
#else
|
||||
#define BOARD_SDMMC_POWER_PIN 10
|
||||
#define BOARD_SDMMC_SLOT 0
|
||||
#endif
|
||||
#define BOARD_SDMMC_POWER_CHANNEL 4
|
||||
#define BOARD_SDMMC_POWER_ON_LEVEL HIGH
|
||||
|
||||
// BT/WIFI - ESP32C6
|
||||
#define BOARD_HAS_SDIO_ESP_HOSTED
|
||||
#ifdef CROWPANEL_ADV_P4_50
|
||||
// CrowPanel Advanced P4 50": 4-bit SDIO on Slot 1 with GPIO 53/54/52/51/50/49
|
||||
#define BOARD_SDIO_ESP_HOSTED_CLK 53
|
||||
#define BOARD_SDIO_ESP_HOSTED_CMD 54
|
||||
#define BOARD_SDIO_ESP_HOSTED_D0 52
|
||||
#define BOARD_SDIO_ESP_HOSTED_D1 51
|
||||
#define BOARD_SDIO_ESP_HOSTED_D2 50
|
||||
#define BOARD_SDIO_ESP_HOSTED_D3 49
|
||||
#define BOARD_SDIO_ESP_HOSTED_RESET 20
|
||||
#else
|
||||
// CrowPanel Advanced P4 70/90/101": 1-bit SDIO on Slot 1 with GPIO 18/19/14/15
|
||||
#define BOARD_SDIO_ESP_HOSTED_CLK 18
|
||||
#define BOARD_SDIO_ESP_HOSTED_CMD 19
|
||||
#define BOARD_SDIO_ESP_HOSTED_D0 14
|
||||
#define BOARD_SDIO_ESP_HOSTED_D1 15
|
||||
#define BOARD_SDIO_ESP_HOSTED_D2 16
|
||||
#define BOARD_SDIO_ESP_HOSTED_D3 17
|
||||
#define BOARD_SDIO_ESP_HOSTED_RESET 32
|
||||
#endif
|
||||
|
||||
#endif /* Pins_Arduino_h */
|
||||
@@ -0,0 +1,194 @@
|
||||
[env:crowpanel-advanced-p4-50]
|
||||
extends = esp32p4_base
|
||||
board = crowpanel-p4
|
||||
board_check = true
|
||||
board_build.partitions = default_16MB.csv
|
||||
|
||||
build_src_filter =
|
||||
${esp32p4_base.build_src_filter}
|
||||
+<../variants/esp32p4/crowpanel-advanced-p4>
|
||||
|
||||
build_flags =
|
||||
${esp32p4_base.build_flags}
|
||||
-D CROWPANEL_ADV_P4
|
||||
-D CROWPANEL_ADV_P4_50
|
||||
-I variants/esp32p4/crowpanel-advanced-p4
|
||||
-D ARDUINO_USB_CDC_ON_BOOT=0
|
||||
-D ARDUINO_USB_MODE=1
|
||||
-D MESHTASTIC_EXCLUDE_WEBSERVER=1
|
||||
-D MESHTASTIC_EXCLUDE_CANNEDMESSAGES=1
|
||||
-D MESHTASTIC_EXCLUDE_INPUTBROKER=1
|
||||
-D MAX_NUM_NODES=500
|
||||
-D MAX_NUM_NODES_VIEW=500
|
||||
; -DSOC_USB_OTG_SUPPORTED=1
|
||||
-D CONFIG_DISABLE_HAL_LOCKS=1 ; "feels" to be a bit more stable without locks
|
||||
; -D INPUTDRIVER_BUTTON_TYPE=0
|
||||
-D HAS_SDCARD
|
||||
-D HAS_SD_MMC
|
||||
-D BOARD_MAX_SDMMC_FREQ=100000
|
||||
-D BOARD_HAS_1BIT_SDMMC
|
||||
-D SD_SCLK_PIN=43
|
||||
-D SD_MISO_PIN=39
|
||||
-D SD_MOSI_PIN=44
|
||||
-D SD_MMC_HOST_SLOT=SDMMC_HOST_SLOT_1
|
||||
-D HAS_SCREEN=1
|
||||
-D HAS_TFT=1
|
||||
; -D USE_I2S_BUZZER
|
||||
-D RAM_SIZE=10240
|
||||
-D LV_LVGL_H_INCLUDE_SIMPLE
|
||||
-D LV_CONF_INCLUDE_SIMPLE
|
||||
-D LV_COMP_CONF_INCLUDE_SIMPLE
|
||||
-D LV_USE_SYSMON=0
|
||||
-D LV_USE_PROFILER=0
|
||||
-D LV_USE_PERF_MONITOR=0
|
||||
-D LV_USE_MEM_MONITOR=0
|
||||
-D LV_USE_LOG=0
|
||||
-D LV_CACHE_DEF_SIZE=3400000
|
||||
-D USE_LOG_DEBUG
|
||||
-D LOG_DEBUG_INC=\"DebugConfiguration.h\"
|
||||
-D RADIOLIB_SPI_PARANOID=0
|
||||
-D LGFX_SCREEN_WIDTH=800
|
||||
-D LGFX_SCREEN_HEIGHT=480
|
||||
-D DISPLAY_SIZE=800x480 ; landscape mode
|
||||
-D LGFX_DRIVER=LGFX_ELECROW_P4_50
|
||||
-D GFX_DRIVER_INC=\"graphics/LGFX/LGFX_ELECROW_P4_50.h\"
|
||||
-D LGFX_BUFSIZE=1152000
|
||||
-D LGFX_SKIP_RGB565_SWAP
|
||||
-D VIEW_320x240
|
||||
-D DISPLAY_SET_RESOLUTION
|
||||
-D USE_PACKET_API
|
||||
-D MAP_FULL_REDRAW
|
||||
|
||||
lib_deps =
|
||||
${esp32p4_base.lib_deps}
|
||||
${device-ui_base.lib_deps}
|
||||
# renovate: datasource=github-tags depName=LovyanGFX packageName=lovyan03/LovyanGFX
|
||||
https://github.com/lovyan03/LovyanGFX/archive/refs/tags/1.2.21.zip
|
||||
|
||||
custom_sdkconfig =
|
||||
${esp32p4_base.custom_sdkconfig}
|
||||
# Disable external SDMMC IO power control to avoid GPIO conflicts
|
||||
CONFIG_SOC_SDMMC_IO_POWER_EXTERNAL=n
|
||||
# CONFIG_BT_BLE_50_FEATURES_SUPPORTED is not set
|
||||
CONFIG_BT_BLE_42_FEATURES_SUPPORTED=y
|
||||
|
||||
# Force hosted transport/target selection for this env.
|
||||
CONFIG_ESP_HOSTED_SDIO_4_BIT_BUS=y
|
||||
CONFIG_ESP_HOSTED_SDIO_CLOCK_FREQ_KHZ=40000
|
||||
# CrowPanel SDIO pins
|
||||
CONFIG_ESP_HOSTED_PRIV_SDIO_PIN_CMD_SLOT_1=54
|
||||
CONFIG_ESP_HOSTED_PRIV_SDIO_PIN_CLK_SLOT_1=53
|
||||
CONFIG_ESP_HOSTED_PRIV_SDIO_PIN_D0_SLOT_1=52
|
||||
CONFIG_ESP_HOSTED_PRIV_SDIO_PIN_D1_4BIT_BUS_SLOT_1=51
|
||||
CONFIG_ESP_HOSTED_PRIV_SDIO_PIN_D2_4BIT_BUS_SLOT_1=50
|
||||
CONFIG_ESP_HOSTED_PRIV_SDIO_PIN_D3_4BIT_BUS_SLOT_1=49
|
||||
|
||||
# Also set resolved SDIO pins explicitly so generated sdkconfig does not
|
||||
# retain stale template values from framework sdkconfig.
|
||||
CONFIG_ESP_HOSTED_SDIO_PIN_CMD=54
|
||||
CONFIG_ESP_HOSTED_SDIO_PIN_CLK=53
|
||||
CONFIG_ESP_HOSTED_SDIO_PIN_D0=52
|
||||
CONFIG_ESP_HOSTED_SDIO_PIN_D1=51
|
||||
CONFIG_ESP_HOSTED_SDIO_PIN_D2=50
|
||||
CONFIG_ESP_HOSTED_SDIO_PIN_D3=49
|
||||
CONFIG_ESP_HOSTED_SDIO_PRIV_PIN_D1_4BIT_BUS=51
|
||||
|
||||
# SDIO Slot 1 via GPIO matrix
|
||||
CONFIG_ESP_HOSTED_SDIO_SLOT_1=y
|
||||
CONFIG_ESP_HOSTED_SDIO_RESET_ACTIVE_HIGH=y
|
||||
CONFIG_ESP_HOSTED_SDIO_GPIO_RESET_SLAVE=20
|
||||
CONFIG_ESP_HOSTED_GPIO_SLAVE_RESET_SLAVE=20
|
||||
|
||||
[env:crowpanel-advanced-p4-70-90-101]
|
||||
extends = esp32p4_base
|
||||
board = esp32-p4-evboard
|
||||
board_check = true
|
||||
board_build.partitions = default_16MB.csv
|
||||
|
||||
build_src_filter =
|
||||
${esp32p4_base.build_src_filter}
|
||||
+<../variants/esp32p4/crowpanel-advanced-p4>
|
||||
|
||||
build_flags =
|
||||
${esp32p4_base.build_flags}
|
||||
-D CROWPANEL_ADV_P4
|
||||
-D CROWPANEL_ADV_P4_70_90_101
|
||||
-I variants/esp32p4/crowpanel-advanced-p4
|
||||
-D ARDUINO_USB_CDC_ON_BOOT=0
|
||||
-D ARDUINO_USB_MODE=1
|
||||
-D MESHTASTIC_EXCLUDE_WEBSERVER=1
|
||||
-D MESHTASTIC_EXCLUDE_CANNEDMESSAGES=1
|
||||
-D MESHTASTIC_EXCLUDE_INPUTBROKER=1
|
||||
-D MAX_NUM_NODES=500
|
||||
-D MAX_NUM_NODES_VIEW=500
|
||||
; -DSOC_USB_OTG_SUPPORTED=1
|
||||
-D CONFIG_DISABLE_HAL_LOCKS=1 ; "feels" to be a bit more stable without locks
|
||||
; -D INPUTDRIVER_BUTTON_TYPE=0
|
||||
-D HAS_SDCARD
|
||||
-D HAS_SD_MMC
|
||||
-D BOARD_MAX_SDMMC_FREQ=10000
|
||||
-D BOARD_HAS_1BIT_SDMMC
|
||||
-D SD_SCLK_PIN=43
|
||||
-D SD_MISO_PIN=39
|
||||
-D SD_MOSI_PIN=44
|
||||
-D SD_MMC_HOST_SLOT=SDMMC_HOST_SLOT_0
|
||||
-D HAS_SCREEN=1
|
||||
-D HAS_TFT=1
|
||||
; -D USE_I2S_BUZZER
|
||||
-D RAM_SIZE=12288
|
||||
-D LV_LVGL_H_INCLUDE_SIMPLE
|
||||
-D LV_CONF_INCLUDE_SIMPLE
|
||||
-D LV_COMP_CONF_INCLUDE_SIMPLE
|
||||
-D LV_USE_SYSMON=1
|
||||
-D LV_USE_PROFILER=0
|
||||
-D LV_USE_PERF_MONITOR=1
|
||||
-D LV_USE_MEM_MONITOR=0
|
||||
-D LV_USE_LOG=0
|
||||
-D LV_CACHE_DEF_SIZE=5600000
|
||||
-D USE_LOG_DEBUG
|
||||
-D LOG_DEBUG_INC=\"DebugConfiguration.h\"
|
||||
-D LORA_SPI_FREQUENCY=1000000
|
||||
-D BOARD_LORA_MAX_TX_POWER=17
|
||||
-D SX126X_CURRENT_LIMIT_MA=100
|
||||
-D RADIOLIB_SPI_PARANOID=0
|
||||
-D LGFX_SCREEN_WIDTH=1024
|
||||
-D LGFX_SCREEN_HEIGHT=600
|
||||
-D DISPLAY_SIZE=1024x600 ; landscape mode
|
||||
-D LGFX_DRIVER=LGFX_ELECROW_P4_70_90_101
|
||||
-D GFX_DRIVER_INC=\"graphics/LGFX/LGFX_ELECROW_P4_70_90_101.h\"
|
||||
-D LGFX_BUFSIZE=614400
|
||||
-D VIEW_320x240
|
||||
-D DISPLAY_SET_RESOLUTION
|
||||
-D USE_PACKET_API
|
||||
; -D MAP_FULL_REDRAW
|
||||
|
||||
lib_deps =
|
||||
${esp32p4_base.lib_deps}
|
||||
${device-ui_base.lib_deps}
|
||||
https://github.com/lovyan03/LovyanGFX#develop
|
||||
|
||||
custom_sdkconfig =
|
||||
${esp32p4_base.custom_sdkconfig}
|
||||
CONFIG_SOC_SDMMC_IO_POWER_EXTERNAL=n
|
||||
# CONFIG_BT_BLE_50_FEATURES_SUPPORTED is not set
|
||||
CONFIG_BT_BLE_42_FEATURES_SUPPORTED=y
|
||||
# SDIO Slot 1 via GPIO matrix
|
||||
CONFIG_ESP_HOSTED_SDIO_SLOT_1=y
|
||||
|
||||
# 1-bit bus (proven stable on CrowPanel)
|
||||
CONFIG_ESP_HOSTED_SDIO_1_BIT_BUS=y
|
||||
CONFIG_ESP_HOSTED_SDIO_BUS_WIDTH=1
|
||||
|
||||
# Conservative 10 MHz clock for OTA reliability
|
||||
CONFIG_ESP_HOSTED_SDIO_CLOCK_FREQ_KHZ=10000
|
||||
|
||||
# CrowPanel SDIO pins (happen to match P4 Slot 1 defaults)
|
||||
CONFIG_ESP_HOSTED_PRIV_SDIO_PIN_CLK_SLOT_1=18
|
||||
CONFIG_ESP_HOSTED_PRIV_SDIO_PIN_CMD_SLOT_1=19
|
||||
CONFIG_ESP_HOSTED_PRIV_SDIO_PIN_D0_SLOT_1=14
|
||||
CONFIG_ESP_HOSTED_PRIV_SDIO_PIN_D1_1BIT_BUS_SLOT_1=15
|
||||
|
||||
# Reset pin: GPIO32, active high (R77 pullup on CrowPanel)
|
||||
CONFIG_ESP_HOSTED_SDIO_RESET_ACTIVE_HIGH=y
|
||||
CONFIG_ESP_HOSTED_SDIO_GPIO_RESET_SLAVE=32
|
||||
CONFIG_ESP_HOSTED_GPIO_SLAVE_RESET_SLAVE=32
|
||||
@@ -0,0 +1,23 @@
|
||||
#include "variant.h"
|
||||
#include "Arduino.h"
|
||||
#include <esp32-hal-periman.h>
|
||||
|
||||
extern "C" void initVariant(void)
|
||||
{
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32P4) && defined(BOARD_SDMMC_POWER_PIN)
|
||||
// Ensure setLDOPower() exits early for this board family.
|
||||
// On ESP32-P4, Arduino SPI may attempt SDMMC on-chip LDO setup when SPI uses
|
||||
// pins that match SDMMC slot0 IOMUX pins (for example, GPIO 47/48 on p4-50).
|
||||
// Pre-tagging BOARD_SDMMC_POWER_PIN as "SDMMC POWER" short-circuits that path.
|
||||
if (perimanPinIsValid(BOARD_SDMMC_POWER_PIN)) {
|
||||
pinMode(BOARD_SDMMC_POWER_PIN, OUTPUT);
|
||||
digitalWrite(BOARD_SDMMC_POWER_PIN, BOARD_SDMMC_POWER_ON_LEVEL);
|
||||
|
||||
if (perimanGetPinBusType(BOARD_SDMMC_POWER_PIN) != ESP32_BUS_TYPE_MAX &&
|
||||
perimanGetPinBusExtraType(BOARD_SDMMC_POWER_PIN) == nullptr) {
|
||||
perimanSetPinBusExtraType(BOARD_SDMMC_POWER_PIN, "");
|
||||
}
|
||||
perimanSetPinBusExtraType(BOARD_SDMMC_POWER_PIN, "SDMMC POWER");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
#define HAS_WIRE 0
|
||||
#define I2C_SDA1 45
|
||||
#define I2C_SCL1 46
|
||||
|
||||
#define USE_POWERSAVE
|
||||
#define WAKE_ON_TOUCH
|
||||
#define SCREEN_TOUCH_INT 42
|
||||
#define SLEEP_TIME 180
|
||||
|
||||
#if defined(CROWPANEL_ADV_P4_50)
|
||||
|
||||
// use UART3-IN for GPS (UART1 can not work with lora)
|
||||
#define GPS_DEFAULT_NOT_PRESENT 1
|
||||
#define GPS_RX_PIN 28
|
||||
#define GPS_TX_PIN 27
|
||||
|
||||
// LoRa
|
||||
#define USE_SX1262
|
||||
#define LORA_SCK 26
|
||||
#define LORA_MISO 47
|
||||
#define LORA_MOSI 48
|
||||
#define LORA_CS 30
|
||||
#define LORA_RESET 32
|
||||
|
||||
#define SX126X_CS LORA_CS
|
||||
#define SX126X_DIO1 31
|
||||
#define SX126X_BUSY 29
|
||||
#define SX126X_RESET LORA_RESET
|
||||
#define SX126X_DIO2_AS_RF_SWITCH
|
||||
#define SX126X_DIO3_TCXO_VOLTAGE 3.3
|
||||
|
||||
#elif defined(CROWPANEL_ADV_P4_70_90_101)
|
||||
|
||||
// use UART1 for GPS
|
||||
#define GPS_DEFAULT_NOT_PRESENT 1
|
||||
#define GPS_RX_PIN 48
|
||||
#define GPS_TX_PIN 47
|
||||
|
||||
// LoRa
|
||||
#define USE_SX1262
|
||||
#define LORA_SCK 8
|
||||
#define LORA_MISO 7
|
||||
#define LORA_MOSI 6
|
||||
#define LORA_CS 10
|
||||
#define LORA_RESET 54
|
||||
|
||||
#define SX126X_CS LORA_CS
|
||||
#define SX126X_DIO1 53
|
||||
#define SX126X_BUSY 9
|
||||
#define SX126X_RESET LORA_RESET
|
||||
#define SX126X_DIO2_AS_RF_SWITCH
|
||||
#define SX126X_DIO3_TCXO_VOLTAGE 3.3
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user