omi
omi copied to clipboard
added button notify characteristic #566
#566 Added button and button characteristic
How to test:
Download the uf2 file for this repository and flash it onto your device. It should be in Friend/firmware. If you can’t find it then you can generate it using the nrf SDK. I used version 2.5.0 for both Download the updated app on your phone. Make sure to enable debug mode. Go to the debug console. Wait until your device connects to the app (blue light.) There are two modes: press for a little bit, then press for about 2 seconds. You should get the notifications Button Pressed and Button long Press, indicating a button tap and Long press respectively. If for some reason you can’t use the friend debugger, then you can simply open the BLE app and press the button described in 3. You should be getting the count.
Quick rundown on how it works
An interrupt service routine is attached to pin D5 according to the Xiao sense diagram. The interrupt triggers when it detects a button press, both pressed up and down. Pin D4 is configured as supply power. When the interrupt triggers, a function is called that checks if the button press is actually a button press (for this implementation, it rejects inputs that appear jittery, or swing in voltage too often. That way only “stable” inputs are considered pressed). Then the input is recorded. A work order occasionally checks the input value. Then we grab the current state of the button, whether it be idle, single pressed, double press, or in a grace period. Depending on the state and if the current GPIO pin reads 1 or 0, it will perform different actions such as notify taps, double taps, presses, etc. I may write a guide later but TLDR it is a finite state machine, that computes an action given its state and event.
Some notes on the button:
I noticed that “fat fingering” the button, which means clicking the button with your skin tends to make the button act unpredictably. The most likely reason may be the contacts on the right side of the button, in which your fingers could accidentally connect the metal and trigger the button. Try to use your fingernails or cover the button with insulation. I imagine that in future versions that you would press the button with some insulation to protect against this (much like Avi’s Friend pendant.) I’ve probably pressed the button about 1000 times, and the solder connecting the button is starting to break. The boards appear to be separating.
What can you build?
Some ideas that were thrown around was attaching APIs according to button events. Also mentioned earlier was a “record” button that could simulate other necklaces on the market. Ideas for implementation are plenty!
Nice, what is missing for it to be stable, can you post here the uf2 file until this point?
Can we detect push down + release button? for testing a long press?
Not sure this will work without overlay pin/device config changes in configuration. Keen to see if this works out of the box without cmake header changes
I used the nrf SDK and followed the instructions on the friend firmware guide to build, and it seems to work for me, so maybe try using that.
I'd suggest we add the logic for determining click | long press | double-click in the firmware, and instead of sending a counter, we send explicit events. Something like this:
00 - neutral
01 - button down
02 - button up
03 - click (sent after button up, if button up within specific time from button down, and no previous click within specific timeout)
04 - double click (sent after button up, if button up within specific time from button down, and there was a previous click within specific timeout)
05 - long press (send after button up, if button up later than the regular click time period from button down)
This way the app can base it's logic on user behavior instead, and leave the hardware events interpretation to the device. (Incidentally, this would make it easy for us to mimic button with other in future hardware revisions)
Also, while there's no second button yet, I'd suggest we make the property two bytes, and use the second byte as a button index, which would allow us to easily add more buttons down the road if necessary.
I'd suggest we add the logic for determining click | long press | double-click in the firmware, and instead of sending a counter, we send explicit events. Something like this:
00 - neutral 01 - button down 02 - button up 03 - click (sent after button up, if button up within specific time from button down, and no previous click within specific timeout) 04 - double click (sent after button up, if button up within specific time from button down, and there was a previous click within specific timeout) 05 - long press (send after button up, if button up later than the regular click time period from button down)This way the app can base it's logic on user behavior instead, and leave the hardware events interpretation to the device. (Incidentally, this would make it easy for us to mimic button with other in future hardware revisions)
Also, while there's no second button yet, I'd suggest we make the property two bytes, and use the second byte as a button index, which would allow us to easily add more buttons down the road if necessary.
Yes, that was my original intention with sending the counter to the device. I was thinking of either having the app interpret the counter as it pleased, with the exception that the hardware would send the long event even if the button was pressed down. I didn't really think anything special should happen when just pressing down or up, as the user would release it anyways expecting something to happen. I do think it is a good idea to implement the double tap, and long press events on the hardware side, and I will get to that part later.
The second button idea is nice, but I would need to know which GPIO pins to use, and seeing as how a ton of pins are being used for the storage / speaker, I think we need to consider the state of the device after all the features we want are implemented before adding more.
The second button idea is nice, but I would need to know which GPIO pins to use Nah, don't make it all the way to the actual hardware. Just make the BLE property have a second byte in its value with the index of the button (which currently will be always 0), that if we make a two-button hardware, we can change accordingly. This way you make it easy to the app to understand a protocol that supports more than one buttons, even if the current hardware iteration only has one.
Yes, that was my original intention with sending the counter to the device. The counter is too close to the raw HW. Depending on the user experience we want to build, the app might change its UI state based on whether the button is down, or up (hence the explicit down and up events), separately from the action that happens on click/double-click/long click. And if tomorrow we change this button to capacitive touch or a touch screen, we can change the firmware logic how the events are recognized, while still keeping the same app logic, as it doesn't care what the underlying HW implementation is.
Basically, make it so the app developers don't have to think much about the hardware. @josancamon19 would appreciate it if you make his life easier. :-)
I won't exactly make give it two button support, but in thinking about what you've suggested, I realized how unreadable my code is, so I have rewritten the code to make it much more readable in case anyone wants to add new button gestures and more buttons. I've also included the double tap feature, although you have to press it a little slow for now. If you follow the instructions above and try to double tap, single tap, long press etc then you can see it in the debug console. The hardware only sends events now too. And button press and undress
I was looking at the firmware_1.5 and how that code deals with buttons, and I noticed it uses k_work_reschedule to deal with the delays instead of the way you are doing it (checking every 40ms). Not sure which way is better though.
I was looking at the firmware_1.5 and how that code deals with buttons, and I noticed it uses k_work_reschedule to deal with the delays instead of the way you are doing it (checking every 40ms). Not sure which way is better though.
Hm, I think our implementations are pretty similar except for the 15 ms delay instead of my 40. As long as they work!
@francip @koconder are we ready to merge this?
Looks to be in good shape to test, ill have a go with the firmware and validate.
@kevvz tested but didn't work for me
Was able to cnnect and run. Installed firmware. clicked the button in tens of different ways
Expected behavior: I see anything in debugger thatshows I clicked the button
Current behaviour: nothing changes in debugger either on short press or long press
@kevvz tested but didn't work for me
Was able to cnnect and run. Installed firmware. clicked the button in tens of different ways
Expected behavior: I see anything in debugger thatshows I clicked the button
Current behaviour: nothing changes in debugger either on short press or long press
![]()
Ah yes, I ran into the same issue when showing this to others. If you want you can use BLE scanner to see the events.
