h2zero / NimBLE-Arduino

A fork of the NimBLE library structured for compilation with Arduino, for use with ESP32, nRF5x.
https://h2zero.github.io/NimBLE-Arduino/
Apache License 2.0
700 stars 145 forks source link

Cannot save a client mac address for the life of me, thus no whitelist #597

Open JustTryingToGetSomeWorkDone opened 11 months ago

JustTryingToGetSomeWorkDone commented 11 months ago

I apologize, this is more a bawling cry for help than a bug. I've been working on getting two esp32-c3s talking to each other. This is my first BLE project, so I went for it in Arduino for simplicity, and I've come a long way in the last few months. I've got a working server and client exchanging data through 4 characteristics. The server is essentially activating "relays" based on the characteristic settings set by the client. I'm worried about security so that random people don't walk up and start flipping switches, so I successfully got encryption working between the two, but since that encryption is based on a weak passkey that could probably be hacked out far too easily anyway, I'd like to set up a whitelist. The devices won't be moving, so random addresses to prevent tracking are unnecessary, I think. Of course mac spoofing is a concern, but bluetooth is not the most secure standard. I'm just trying to make sure it isn't easy to interfere with. So, I should be able to accomplish this goal with my current IDE and libraries (barely an IDE but still).

First let me say that the dearth of information on doing ANYTHING with BLE is insane. I finally switched from Bluedroid to NimBLE, and my progress has sped up tremendously. Thank you very much for actually documenting your library. Unfortunately, the documentation around whitelists with NimBLE is lacking, in my opinion.

I have very basic questions that I cannot find answers to. Stack Overflow ignores all esp32 based BLE questions, Arduino forums ignore all BLE questions in general, and Espressif forums ignore all BLE questions period. It's as if BLE is a steamy pile of crap that no one wants to touch. I wonder why...

Let me stop venting and get to some questions. I've made zero progress in about 48 hours of coding, so I can't keep going like this.

I'll post the pertinent parts of my code with lots of comments describing my thought process, goals, and results. If that's not enough, please ask and I'll explain anything in excruciating detail.

Start off prototyping the NimBLEAddress variable that will hold the client mac address.. starting with 1 hardcoded address till it works

NimBLEAddress WLaddress1 = NimBLEAddress("00:00:00:00:00:00"); // or is it better to use("")? Neither way works.

Later we setup server callbacks, and within that class is this callback:

void onAuthenticationComplete (NimBLEServer *pServer, ble_gap_conn_desc *desc)
   {
    //WLaddress1 = new (WLaddress1) NimBLEAddress(desc->peer_ota_addr); 
    // I couldn't get this method to work at all

    WLaddress1 = NimBLEAddress(desc->peer_ota_addr); 
    // This compiles but doesn't seem to work.

    NimBLEDevice::whiteListAdd(WLaddress1); 
    //This is an evolution of NimBLEDevice::whiteListAdd(NimBLEAddress(desc->peer_ota_addr); 
    //which works for whitelisting till the esp is reset, then useless.

    preferences.putBytes("wladdress1", &WLaddress1, sizeof(NimBLEAddress)); 
    //This never seems to actually work. I've tried half a dozen ways. OR it does work, and retrieving doesn't work? Or neither works.
    //preferences.putBytes("wladdress1", &WLaddress1, sizeof(WLaddress1)); 
    //I tried this way too, it doesn't work either. replacing the length with a 6, 7, 17, nothing works. I would like to just see the data once then maybe I can hack something together. I have no idea what a BLEAddress actually looks like. So far, in my experience, it looks like "0"

    Serial.println("#######onauthcallback address: "); // These 2 prints never send anything to the serial monitor, why? I know the callback is firing and doing something cause the whitelist works till reset.. maybe blocking calls are not allowed? Cannot find information specific to this callback anywhere. Other callbacks do allow prints, so who knows.

    Serial.println(WLaddress1);  // always returns 0 in other places, in this callback, never returns anything.
    }

Later in setup() I start the serial monitor (only while trying to debug this stuff otherwise it gets in the way of BLE in my experience, then load my preferences NVS saved data:

    preferences.begin("savedstuff", false);
    //[...]
    WLaddress1 = preferences.getBytes("wladdress1", &WLaddress1, sizeof(NimBLEAddress)); // I only added this "WLaddress1 = " recently. I don't think it's needed with getBytes. Didn't work either way.
    Serial.println("#######setupload address: "); // this one works
    Serial.println(WLaddress1); // this only ever returns 0

Then a little further down after all my server setup and security settings while still in setup() :

if (digitalRead(pin1) == LOW && digitalRead(pin2) == LOW && digitalRead(pin3) == LOW) // if these 3 pins are low, anyone can connect
          {
            pAdvertising->setScanFilter(false,false); //scanning is not filtered, connecting is not filtered
            pAdvertising->start();
          }
    else
          {
            NimBLEDevice::whiteListAdd(NimBLEAddress(WLaddress1)); // this never works, but of course not, the loaded address is == 0.
            Serial.println("#######loadaddbeforescanfilt address: "); // this one works
            Serial.println(WLaddress1); // this one says 0

            pAdvertising->setScanFilter(true,true); //scanning is filtered, connecting is filtered
            pAdvertising->start();

          }

(I have a similar conditional setup for the onDisconnect callback which seems to work as far as changing the filtering settings)

This is pretty much the end of it. The rest is my loop() code which checks for button presses and some stuff dealing with my "relays".

This is basically where I find myself stuck. I'm more of a hardware person than software, so please forgive stupid mistakes I may be making. I'm 100% self taught, so I have gaps.

Oh, here's another snippet I tried when getByte-ing things back from preferences, but it never worked right either:

  size_t schLen = preferences.getBytes("wladdress1", NULL, NULL);

  NimBLEAddress buffer[schLen]; // prepare a buffer for the data... didn't like being char, doesn't like being NimBLEAddress

  preferences.getBytes("wladdress1", buffer, schLen);
  memcpy(WLaddress1, buffer, schLen); 

I get an infuriating error from memcpy at times. I don't even understand how a variable can be void... It's supposed to just be copying bytes of memory.. how can it screw that up? This is one of my gaps.

So in summary, I want to use a whitelist that persists across resets, so I THINK I need to save my whitelist to NVS, but I can't get my whitelist or any mac address to save into NVS. I cannot find sufficient information on how to do this. I've come across half a dozen code snippets strewn across various forums and chat rooms (ugh what a hassle that was), but they never seem to work together. I would like a bit more detail on how to go about this, maybe a few example code snippets of saving a mac address (the currently connected device would be perfect) and especially loading that back into a whitelist from NVS upon reset. Best places to place these methods would be helpful as well.

JustTryingToGetSomeWorkDone commented 11 months ago

I thought inline comments to my code would make it easier to read... then it made a scroll bar. I tried to clean some of it up, but it's hard to refer to lines of code with comments that are far away from the line of code they're referring to... apologies.

Update: I've started digging into the source of NimBLE and I was quite surprised to see it contains more detailed documentation than any other source I've been looking at. I should have switched to NimBLE a long time ago. I'll keep digging and keep asking ChatGPT for help, and I'll report back if I find success. I really hate type conversions, and it's very frustrating that BLE Address types and ESP Preferences types don't overlap better than they do. Perhaps enough for some, but I'm not there yet. I especially hate having to do anything bit by bit or byte by byte. I feel like I should just bust out a slide rule and graph paper if I'm going to bother programming like that.

h2zero commented 11 months ago

Hello, just a quick comment. This: Serial.println(WLaddress1); will not work as you may expect. Instead you will want to use Serial.println(WLaddress1.toString().c_str());.

As for storing the address I think you may need to use desc->peer_id_addr instead of desc->peer_ota_addr but you'll need to check in the callback by printing each of them as described above.

JustTryingToGetSomeWorkDone commented 11 months ago

Thank you very much. I have been grasping at straws for the last couple of my programming days, so I tried a large variety of methods as getting anything output. Even if it was garbage, I wanted to see something just to get a little progress and find a direction to start moving forward again. The tip about peer_id_addr should indeed come in handy.

I'll give it a try and get back to you.

JustTryingToGetSomeWorkDone commented 11 months ago

Well, after a few hours attempting to get it to work with varying combinations of peer_ota_addr and peer_id_addr, the only real progress I've made is being able to store NimBLEAddress-like strings in Preferences. I couldn't get the actual address of the connected device, but I could store and retrieve a static address declared in code. I'll eventually come back to this I imagine, but for now I'm moving it to the bottom of my todo list. There's just too little documentation for me and my skill level to understand where I'm going wrong. Thanks for your help. I might be back one day.