nkolban / esp32-snippets

Sample ESP32 snippets and code fragments
https://leanpub.com/kolban-ESP32
Apache License 2.0
2.37k stars 710 forks source link

Cannot connect reliably using this library to a windows 11 machine #1125

Open codewitch-honey-crisis opened 2 years ago

codewitch-honey-crisis commented 2 years ago

I've paired successfully. I am simply trying to send MIDI Instead of successfully establishing a connection, I get this nonsense on the serial port, and it never fires the connect callback. Windows seems to think it's connected, however

lld_pdu_get_tx_flush_nb HCI packet count mismatch (0, 1)
lld_pdu_get_tx_flush_nb HCI packet count mismatch (0, 1)
lld_pdu_get_tx_flush_nb HCI packet count mismatch (0, 1)
lld_pdu_get_tx_flush_nb HCI packet count mismatch (0, 1)
lld_pdu_get_tx_flush_nb HCI packet count mismatch (0, 1)
lld_pdu_get_tx_flush_nb HCI packet count mismatch (0, 1)
lld_pdu_get_tx_flush_nb HCI packet count mismatch (0, 1)
lld_pdu_get_tx_flush_nb HCI packet count mismatch (0, 1)
lld_pdu_get_tx_flush_nb HCI packet count mismatch (0, 1)

The worst part is, about 1 in every 10 times it works. Frankly, I don't like the way you call new and delete everywhere either. Heap fragmentation is a thing, and the struggle is real. This library is huge and slow and I don't how much of that has to do with what you're wrapping, but between the problems I'm having with it, and the time it takes to actually upload and test due to the size, it makes developing painful. Sometimes less is more.

Anyway, here's my code. I take it I must be doing something wrong. Forgive my deinit routine. I'm sure it doesn't work yet. I don't know what you allocated that I have to free.

#ifndef SFX_NO_BLUETOOTH
#ifdef ESP32
#include <functional>
#include <BLEUtils.h>
#include <sfx_midi_server_ble.hpp>

const std::string MIDI_SERVICE_UUID = "03b80e5a-ede8-4b33-a751-6ce34ec4c700";
const std::string MIDI_CHARACTERISTIC_UUID = "7772e5db-3868-4112-a1a9-f2669d106bf3";

namespace sfx {
template< class T > struct midi_server_ble_remove_reference      { typedef T type; };
template< class T > struct midi_server_ble_remove_reference<T&>  { typedef T type; };
template< class T > struct midi_server_ble_remove_reference<T&&> { typedef T type; };
template <typename T>
typename midi_server_ble_remove_reference<T>::type&& midi_server_ble_move(T&& arg) {
    return static_cast<typename midi_server_ble_remove_reference<T>::type&&>(arg);
}
class ServerCallback : public BLEServerCallbacks,BLECharacteristicCallbacks {
    public:
    ServerCallback(std::function<void(bool)> onConnectedCallback,std::function<void(uint8_t *, uint8_t)> onWriteCallback);
    private:
    void onConnect(BLEServer* pServer) override;
    void onDisconnect(BLEServer* pServer) override;   
    void onWrite(BLECharacteristic *pCharacteristic);
    std::function<void(bool)> onConnectedCallback;
    std::function<void(uint8_t *, uint8_t)> onWriteCallback = nullptr;
};
ServerCallback::ServerCallback(std::function<void(bool)> onConnectedCallback,std::function<void(uint8_t *, uint8_t)> onWriteCallback) : onConnectedCallback(onConnectedCallback),onWriteCallback(onWriteCallback) {}
void ServerCallback::onConnect(BLEServer* server) {
    Serial.println("connected");
    onConnectedCallback(true);
}
void ServerCallback::onDisconnect(BLEServer* server) {
    Serial.println("disconnected");
    onConnectedCallback(false);
    server->startAdvertising();
}
void ServerCallback::onWrite(BLECharacteristic *pCharacteristic) {
    std::string rxValue = pCharacteristic->getValue();

    if (rxValue.length() > 0 && onWriteCallback != nullptr)
        onWriteCallback((uint8_t *)rxValue.c_str(), rxValue.length());

    vTaskDelay(0);  // We leave some time for the IDLE task call esp_task_wdt_reset_watchdog
                    // See comment from atanisoft here : https://github.com/espressif/arduino-esp32/issues/2493
}
void midi_server_ble::copy(midi_server_ble& rhs) {
    int8_t sp = rhs.m_clock.source_pin();
    rhs.m_clock.source_pin(-1);
    m_clock=midi_server_ble_move(m_clock);
    m_initialized = rhs.m_initialized;
    m_connected = rhs.m_connected;
    m_pchar = rhs.m_pchar;
    rhs.m_pchar = nullptr;
    m_pcb = rhs.m_pcb;
    rhs.m_pcb = nullptr;
    m_pdesc = rhs.m_pdesc;
    rhs.m_pdesc = nullptr;
    m_psvc = rhs.m_psvc;
    rhs.m_psvc = nullptr;
    m_psrv = rhs.m_psrv;
    rhs.m_psrv = nullptr;
    m_queue = rhs.m_queue;
    rhs.m_queue = nullptr;
    m_queue_capacity = rhs.m_queue_capacity;
    m_queue_size = rhs.m_queue_size;
    m_queue_index = rhs.m_queue_index;
    m_clock.source_pin(sp);
}
midi_server_ble::midi_server_ble() : m_initialized(false),
                                    m_connected(false),
                                    m_pchar(nullptr),
                                    m_pcb(nullptr),
                                    m_pdesc(nullptr),
                                    m_psvc(nullptr),
                                    m_psrv(nullptr),
                                    m_queue(nullptr),
                                    m_queue_capacity(0),
                                    m_queue_size(0),
                                    m_queue_index(0) {

}
midi_server_ble::~midi_server_ble() {
    deinitialize();
}
sfx_result midi_server_ble::send_packet(uint8_t *data, size_t length) {
    if(connected()) {
        m_pchar->setValue(data,length);
        m_pchar->notify();
        return sfx_result::success;
    }
    return sfx_result::invalid_state;
}
void midi_server_ble::receive_packet(uint8_t *data, size_t length) {
    Serial.println("received packet");
}

sfx_result midi_server_ble::initialize(const char *device_name) {
    if (!m_initialized) {
        m_pcb = new ServerCallback(
            [this](bool connected){this->m_connected=connected; },
            [this](uint8_t *data, uint8_t size) { this->receive_packet(data, size); }
        );
        if(m_pcb==nullptr) {
            return sfx_result::out_of_memory;
        }
        BLE2902* pd = new BLE2902();
        if(pd==nullptr) {
            delete (ServerCallback*)m_pcb;
            return sfx_result::out_of_memory;
        }

        BLEDevice::init(device_name);
        BLEServer *pServer = BLEDevice::createServer();
        m_psrv = pServer;
        pServer->setCallbacks((BLEServerCallbacks*)m_pcb);
        BLEService *pService = pServer->createService(BLEUUID(MIDI_SERVICE_UUID));
        auto pCharacteristic = pService->createCharacteristic(
            BLEUUID(MIDI_CHARACTERISTIC_UUID),
            BLECharacteristic::PROPERTY_READ |
                BLECharacteristic::PROPERTY_WRITE |
                BLECharacteristic::PROPERTY_NOTIFY |
                BLECharacteristic::PROPERTY_WRITE_NR);
        m_pchar = pCharacteristic;

        m_pdesc = pd;
        pCharacteristic->addDescriptor(pd);
        pCharacteristic->setCallbacks((BLECharacteristicCallbacks*)m_pcb);

        pService->start();
        m_psvc=pService;
        m_initialized = true;
        BLEAdvertising *pAdvertising = pServer->getAdvertising();
        pAdvertising->addServiceUUID(pService->getUUID());
        pAdvertising->start();
        m_clock.tick_callback(on_tick,this);
        m_clock.start();

    }
    return sfx_result::success;
}
sfx_result midi_server_ble::deinitialize() {

    if(m_initialized) {
        m_clock.stop();
        if(!m_connected && m_psrv!=nullptr) {
            BLEAdvertising *pAdvertising = ((BLEServer*)m_psrv)->getAdvertising();
            pAdvertising->stop();
        }
        if(m_psvc!=nullptr) {
            ((BLEServer*)m_psrv)->removeService((BLEService*)m_psvc);
        }
        BLEDevice::deinit();
        m_connected = false;
    }
    if(m_queue!=nullptr) {
        free(m_queue);
        m_queue = nullptr;
    }
    if(m_psrv!=nullptr) {
        delete (BLEServer*)m_psrv;
        m_psrv = nullptr;
    }
    if(m_pcb!=nullptr) {
        delete (ServerCallback*)m_pcb;
        m_pcb = nullptr;
    }
    if(m_pdesc!=nullptr) {
        delete (BLE2902*)m_pdesc;
        m_pdesc  =nullptr;
    }
    if(m_psvc!=nullptr) {
        delete (BLEService*)m_psvc;
        m_psvc = nullptr;
    }

    return sfx_result::success;
}
sfx_result midi_server_ble::send(const midi_message& msg) {
    auto t = millis();
    uint8_t headerByte = (1 << 7) | ((t >> 7) & ((1 << 6) - 1));
    uint8_t timestampByte = (1 << 7) | (t & ((1 << 7) - 1));
    uint8_t packet[] = {headerByte,timestampByte,msg.status,msg.lsb(),msg.msb()};

    switch(msg.type()) {
    case midi_message_type::note_off:
    case midi_message_type::note_on:
    case midi_message_type::polyphonic_pressure:
    case midi_message_type::control_change:
    case midi_message_type::pitch_wheel_change:
    case midi_message_type::song_position:
        return send_packet(packet,5);
    case midi_message_type::program_change:
    case midi_message_type::channel_pressure:
    case midi_message_type::song_select:
        return send_packet(packet,4);
    case midi_message_type::system_exclusive:
        return sfx_result::not_supported;
    default:
        return send_packet(packet,3);
    }
}
int16_t midi_server_ble::timebase() const {
    return m_clock.timebase();
}
void midi_server_ble::timebase(int16_t value) {
    return m_clock.timebase(value);
}
int32_t midi_server_ble::microtempo() const {
    return m_clock.microtempo();
}
void midi_server_ble::microtempo(int32_t value) {
    m_clock.microtempo(value);
}
unsigned long long midi_server_ble::elapsed() const {
    return m_clock.elapsed_ticks();
}
size_t midi_server_ble::queued() const {
    return m_queue_size;
}
sfx_result midi_server_ble::send(const midi_event& event) {
    if(event.delta<=0) {
        return send(event.message);
    }
    return queue_push_back(event.delta+elapsed(),event.message)?sfx_result::success:sfx_result::out_of_memory;
}
sfx_result midi_server_ble::start() {
    m_clock.start();
    return sfx_result::success;
}
sfx_result midi_server_ble::stop() {
    m_clock.stop();
    return sfx_result::success;
}
sfx_result midi_server_ble::update() {
    m_clock.update();
    return sfx_result::success;
}
bool midi_server_ble::queue_push_back(unsigned long long position,const midi_message& message) {
    const size_t new_cap = m_queue_capacity*2;
    if(m_queue_size==m_queue_capacity) {
        queue_entry* p = (queue_entry*)realloc(m_queue,new_cap*sizeof(queue_entry));
        if(nullptr==p) {
            return false;
        }
        m_queue = p;
        if(m_queue_index>0) {
            memmove(m_queue+m_queue_index,m_queue,(m_queue_capacity-m_queue_index)*sizeof(queue_entry));
            memmove(m_queue,m_queue+(m_queue_capacity-m_queue_index),(m_queue_index)*sizeof(queue_entry));
            m_queue_index = 0;
        }
        m_queue_capacity = new_cap;
    }
    queue_entry qe;
    qe.position = position;
    qe.message = midi_server_ble_move(message);
    m_queue[(m_queue_size+m_queue_index)%m_queue_capacity]=qe;
    ++m_queue_size;
    return true;
}
midi_server_ble::queue_entry* midi_server_ble::queue_peek_front() {
    if(m_queue_size==0) {
        return nullptr;
    }
    return m_queue+m_queue_index;

}
bool midi_server_ble::queue_pop_front(unsigned long long* out_position,midi_message* out_message) {
    if(m_queue_size==0) {
        return false;
    }
    queue_entry* pqe = m_queue+m_queue_index;
    if(nullptr!=out_position) {
        *out_position = pqe->position;
    }
    if(nullptr!=out_message) {
        *out_message = midi_server_ble_move(pqe->message);
    }
    --m_queue_size;
    return true;
}
void midi_server_ble::on_tick(uint32_t pending,unsigned long long elapsed,void* state) {
    midi_server_ble* tp = (midi_server_ble*)state;
    while(true) {
        queue_entry* pqe = tp->queue_peek_front();
        if(pqe==nullptr || pqe->position>elapsed) {
            break;
        }
        midi_message msg;
        tp->queue_pop_front(nullptr,&msg);
        // is meta event?
        if(msg.type()==midi_message_type::meta_event && msg.meta.data!=nullptr) {
            // is midi tempo change message?
            if(msg.meta.type==0x51) {
                tp->microtempo((msg.meta.data[0] << 16) | (msg.meta.data[1] << 8) | msg.meta.data[2]);
            }
        } else {
            tp->send(msg);
        }
    }

}

}  // namespace sfx

#endif  // ESP32
#endif