_blogs
// blogs / 20260517.md
// blogs / 20260517.md
Dev Log: The BLE Rabbit Hole
Overview
Today was one of those days where I spent about 80% of my time wrestling with Bluetooth Low Energy (BLE) and the remaining 20% wondering why I didn't just stick to web dev. Most of my energy went into getting the local control mode for my smart lock project to actually behave, both on the hardware side and in the mobile app.
What I Worked On
The Firmware "Gotcha"
I spent way too long debugging why my phone wouldn't show the pairing dialog when trying to connect to the microcontroller. It turns out I had a classic initialization order bug in the firmware. I was trying to set up security parameters and the passkey before initializing the BLE stack. In the NimBLE world, if you don't call init first, your security settings basically get wiped into the void.
I also added a call to explicitly start the security handshake as soon as a client connects. Android and Windows can be a bit stubborn about initiating pairing on their own, so forcing the issue from the device side seems to have smoothed things over. Now, the PIN prompt actually pops up like it's supposed to.
Making Local Mode Reliable
On the mobile side, I focused on making the "Local Control" feel less brittle. I implemented a background reconnection loop that checks the status every six seconds when the app is in local mode. If the connection drops, it tries to silently re-establish it without bugging the user.
I also had to deal with the perennial headache that is Android permissions. I had to tweak the manifest to ensure location permissions were handled correctly so the BLE scan doesn't just fail silently on newer Android versions. It's annoying that "finding a local device" is tied so closely to "tracking your location," but that's the world we live in.
UI Polish and "Tap to Connect"
I spent some time in the CSS trenches today too. I added some visual distinction between the cloud and local modes—basically giving the local mode its own blue-tinted theme and status badges so it's obvious how you're connected.
One small but important UX change: if the local connection fails, the status card now becomes clickable. Instead of just seeing an "Offline" message, you can just tap the card to manually trigger a re-scan and connection. It feels a lot more intentional than just waiting for an auto-connect that might be struggling.
Wrapping Up
It’s finally starting to feel like a real product. The transition between cloud and local control is getting smoother, even if BLE remains a temperamental beast. Tomorrow I’ll probably look into cleaning up the secret management for the build environment—I realized I was trailing some config files that really shouldn't be tracked in version control.
Catch you tomorrow.