1*1c60b9acSAndroid Build Coastguard Worker# LWS GPIO Button class drivers 2*1c60b9acSAndroid Build Coastguard Worker 3*1c60b9acSAndroid Build Coastguard WorkerLws provides an GPIO button controller class, this centralizes handling a set of 4*1c60b9acSAndroid Build Coastguard Workerup to 31 buttons for resource efficiency. Each controller has two OS timers, 5*1c60b9acSAndroid Build Coastguard Workerone for interrupt to bottom-half event triggering and another that runs at 5ms 6*1c60b9acSAndroid Build Coastguard Workerintervals only when one or more button is down. 7*1c60b9acSAndroid Build Coastguard Worker 8*1c60b9acSAndroid Build Coastguard WorkerEach button has its own active level control and sophisticated state tracking; 9*1c60b9acSAndroid Build Coastguard Workereach button can apply its own classification regime, to allow for different 10*1c60b9acSAndroid Build Coastguard Workerphysical button characteristics, if not overridden a default one is provided. 11*1c60b9acSAndroid Build Coastguard Worker 12*1c60b9acSAndroid Build Coastguard WorkerBoth the controller and individual buttons specify names that are used in the 13*1c60b9acSAndroid Build Coastguard WorkerJSON events produced when the buttons perform actions. 14*1c60b9acSAndroid Build Coastguard Worker 15*1c60b9acSAndroid Build Coastguard Worker## Button electronic to logical event processing 16*1c60b9acSAndroid Build Coastguard Worker 17*1c60b9acSAndroid Build Coastguard WorkerButtons are monitored using GPIO interrupts since this is very cheap in the 18*1c60b9acSAndroid Build Coastguard Workerusual case no interaction is ongoing. There is assumed to be one interrupt 19*1c60b9acSAndroid Build Coastguard Workerper GPIO, but they are pointed at the same ISR, with an opaque pointer to an 20*1c60b9acSAndroid Build Coastguard Workerinternal struct passed per-interrupt to differentiate them and bind them to a 21*1c60b9acSAndroid Build Coastguard Workerparticular button. 22*1c60b9acSAndroid Build Coastguard Worker 23*1c60b9acSAndroid Build Coastguard WorkerThe interrupt is set for notification of the active-going edge, usually if 24*1c60b9acSAndroid Build Coastguard Workerthe button is pulled-up, that's the downgoing edge only. This avoids any 25*1c60b9acSAndroid Build Coastguard Workerambiguity about the interrupt meaning, although oscillation is common around 26*1c60b9acSAndroid Build Coastguard Workerthe transition region when the signal is becoming inactive too. 27*1c60b9acSAndroid Build Coastguard Worker 28*1c60b9acSAndroid Build Coastguard WorkerAn OS timer is used to schedule a bottom-half handler outside of interrupt 29*1c60b9acSAndroid Build Coastguard Workercontext. 30*1c60b9acSAndroid Build Coastguard Worker 31*1c60b9acSAndroid Build Coastguard WorkerTo combat commonly-seen partial charging of the actual and parasitic network 32*1c60b9acSAndroid Build Coastguard Workeraround the button causing drift and oscillation, the bottom-half briefly drives 33*1c60b9acSAndroid Build Coastguard Workerthe button signal to the active level, forcing a more deterministic charge level 34*1c60b9acSAndroid Build Coastguard Workerif it reached the point the interrupt was triggered. This removes much of the 35*1c60b9acSAndroid Build Coastguard Workerunpredictable behaviour in the us range. It would be better done in the ISR 36*1c60b9acSAndroid Build Coastguard Workerbut many OS apis cannot perform GPIO operations in interrupt context. 37*1c60b9acSAndroid Build Coastguard Worker 38*1c60b9acSAndroid Build Coastguard WorkerThe bottom-half makes sure a monitoring timer is enabled, by refcount. This 39*1c60b9acSAndroid Build Coastguard Workeris the engine of the rest of the classification while any button is down. The 40*1c60b9acSAndroid Build Coastguard Workermonitoring timer happens per OS tick or 5ms, whichever is longer. 41*1c60b9acSAndroid Build Coastguard Worker 42*1c60b9acSAndroid Build Coastguard Worker## Declaring button controllers 43*1c60b9acSAndroid Build Coastguard Worker 44*1c60b9acSAndroid Build Coastguard WorkerAn array of button map elements if provided first mapping at least GPIOs to 45*1c60b9acSAndroid Build Coastguard Workerbutton names, and also optionally the classification regime for that button. 46*1c60b9acSAndroid Build Coastguard Worker 47*1c60b9acSAndroid Build Coastguard WorkerThen the button controller definition which points back to the button map. 48*1c60b9acSAndroid Build Coastguard Worker 49*1c60b9acSAndroid Build Coastguard Worker``` 50*1c60b9acSAndroid Build Coastguard Workerstatic const lws_button_map_t bcm[] = { 51*1c60b9acSAndroid Build Coastguard Worker { 52*1c60b9acSAndroid Build Coastguard Worker .gpio = GPIO_NUM_0, 53*1c60b9acSAndroid Build Coastguard Worker .smd_interaction_name = "user" 54*1c60b9acSAndroid Build Coastguard Worker }, 55*1c60b9acSAndroid Build Coastguard Worker}; 56*1c60b9acSAndroid Build Coastguard Worker 57*1c60b9acSAndroid Build Coastguard Workerstatic const lws_button_controller_t bc = { 58*1c60b9acSAndroid Build Coastguard Worker .smd_bc_name = "bc", 59*1c60b9acSAndroid Build Coastguard Worker .gpio_ops = &lws_gpio_plat, 60*1c60b9acSAndroid Build Coastguard Worker .button_map = &bcm[0], 61*1c60b9acSAndroid Build Coastguard Worker .active_state_bitmap = 0, 62*1c60b9acSAndroid Build Coastguard Worker .count_buttons = LWS_ARRAY_SIZE(bcm), 63*1c60b9acSAndroid Build Coastguard Worker}; 64*1c60b9acSAndroid Build Coastguard Worker 65*1c60b9acSAndroid Build Coastguard Worker struct lws_button_state *bcs; 66*1c60b9acSAndroid Build Coastguard Worker 67*1c60b9acSAndroid Build Coastguard Worker bcs = lws_button_controller_create(context, &bc); 68*1c60b9acSAndroid Build Coastguard Worker if (!bcs) { 69*1c60b9acSAndroid Build Coastguard Worker lwsl_err("%s: could not create buttons\n", __func__); 70*1c60b9acSAndroid Build Coastguard Worker goto spin; 71*1c60b9acSAndroid Build Coastguard Worker } 72*1c60b9acSAndroid Build Coastguard Worker``` 73*1c60b9acSAndroid Build Coastguard Worker 74*1c60b9acSAndroid Build Coastguard WorkerThat is all that is needed for init, button events will be issued on lws_smd 75*1c60b9acSAndroid Build Coastguard Workerwhen buttons are pressed. 76*1c60b9acSAndroid Build Coastguard Worker 77*1c60b9acSAndroid Build Coastguard Worker### Regime settings 78*1c60b9acSAndroid Build Coastguard Worker 79*1c60b9acSAndroid Build Coastguard WorkerThe classification regime is designed to reflect both the user interaction 80*1c60b9acSAndroid Build Coastguard Workerstyle and the characteristics of a particular type of button. 81*1c60b9acSAndroid Build Coastguard Worker 82*1c60b9acSAndroid Build Coastguard WorkerMember|Default|Meaning 83*1c60b9acSAndroid Build Coastguard Worker---|---|--- 84*1c60b9acSAndroid Build Coastguard Workerms_min_down|20ms|Down events shorter than this are ignored 85*1c60b9acSAndroid Build Coastguard Workerms_min_down_longpress|300ms|Down events longer than this are reported as a long-click 86*1c60b9acSAndroid Build Coastguard Workerms_up_settle|20ms|After the first indication a button is no longer down, the button is ignored for this interval 87*1c60b9acSAndroid Build Coastguard Workerms_doubleclick_grace|120ms|The time allowed after a click to see if a second, double-click, is forthcoming 88*1c60b9acSAndroid Build Coastguard Workerms_repeat_down|0 / disabled|If held down, interval at which to issue `stilldown` events 89*1c60b9acSAndroid Build Coastguard Workerflags|LWSBTNRGMFLAG_CLASSIFY_DOUBLECLICK|Control which classifications can apply 90*1c60b9acSAndroid Build Coastguard Worker 91*1c60b9acSAndroid Build Coastguard Worker### lws_smd System Message Distribution Events 92*1c60b9acSAndroid Build Coastguard Worker 93*1c60b9acSAndroid Build Coastguard WorkerThe button controller emits system messages of class `LWSSMDCL_INTERACTION`, 94*1c60b9acSAndroid Build Coastguard Workerusing a JSON formatted payload 95*1c60b9acSAndroid Build Coastguard Worker 96*1c60b9acSAndroid Build Coastguard Worker``` 97*1c60b9acSAndroid Build Coastguard Worker{ 98*1c60b9acSAndroid Build Coastguard Worker "type": "button", 99*1c60b9acSAndroid Build Coastguard Worker "src": "controller-name/button-name", 100*1c60b9acSAndroid Build Coastguard Worker "event": "event-name" 101*1c60b9acSAndroid Build Coastguard Worker} 102*1c60b9acSAndroid Build Coastguard Worker``` 103*1c60b9acSAndroid Build Coastguard Worker 104*1c60b9acSAndroid Build Coastguard WorkerFor example, `{"type":"button","src":"bc/user","event":"doubleclick"}` 105*1c60b9acSAndroid Build Coastguard Worker 106*1c60b9acSAndroid Build Coastguard WorkerJSON is used because it is maintainable, extensible, self-documenting and does 107*1c60b9acSAndroid Build Coastguard Workernot require a central, fragile-against-versioning specification of mappings. 108*1c60b9acSAndroid Build Coastguard WorkerUsing button names allows the same code to adapt to different hardware or 109*1c60b9acSAndroid Build Coastguard Workerbutton mappings. Button events may be synthesized for test or other purposes 110*1c60b9acSAndroid Build Coastguard Workercleanly and clearly. 111*1c60b9acSAndroid Build Coastguard Worker 112*1c60b9acSAndroid Build Coastguard WorkerAll the events are somewhat filtered, too short glitches from EMI or whatever 113*1c60b9acSAndroid Build Coastguard Workerare not reported. "up" and "down" events are reported for the buttons in case 114*1c60b9acSAndroid Build Coastguard Workerthe intention is the duration of the press is meaningful to the user code, but 115*1c60b9acSAndroid Build Coastguard Workermore typically the user code wants to consume a higher-level classification of 116*1c60b9acSAndroid Build Coastguard Workerthe interaction, eg, that it can be understood as a single "double-click" event. 117*1c60b9acSAndroid Build Coastguard Worker 118*1c60b9acSAndroid Build Coastguard WorkerEvent name|Meaning 119*1c60b9acSAndroid Build Coastguard Worker---|--- 120*1c60b9acSAndroid Build Coastguard Workerdown|The button passes a filter for being down, useful for duration-based response 121*1c60b9acSAndroid Build Coastguard Workerstilldown|The regime can be configured to issue "repeat" notifications at intervals 122*1c60b9acSAndroid Build Coastguard Workerup|The button has come up, useful for duration-based response 123*1c60b9acSAndroid Build Coastguard Workerclick|The button activity resulted in a classification as a single-click 124*1c60b9acSAndroid Build Coastguard Workerlongclick|The button activity resulted in a classification as a long-click 125*1c60b9acSAndroid Build Coastguard Workerdoubleclick|The button activity resulted in a classification as a double-click 126*1c60b9acSAndroid Build Coastguard Worker 127*1c60b9acSAndroid Build Coastguard WorkerSince double-click detection requires delaying click reporting until it becomes 128*1c60b9acSAndroid Build Coastguard Workerclear a second click isn't coming, it is enabled as a possible classification in 129*1c60b9acSAndroid Build Coastguard Workerthe regime structure and the regime structure chosen per-button. 130*1c60b9acSAndroid Build Coastguard Worker 131*1c60b9acSAndroid Build Coastguard WorkerTypically user code is interested in, eg, a high level classification of what 132*1c60b9acSAndroid Build Coastguard Workerthe button is doing, eg, a "click" event on a specific button. Rather than 133*1c60b9acSAndroid Build Coastguard Workerperform a JSON parse, these events can be processed as strings cheaply using 134*1c60b9acSAndroid Build Coastguard Worker`lws_json_simple_strcmp()`, it's dumb enough to be cheap but smart enough to 135*1c60b9acSAndroid Build Coastguard Workerunderstand enough JSON semantics to be accurate, while retaining the ability to 136*1c60b9acSAndroid Build Coastguard Workerchange and extend the JSON, eg 137*1c60b9acSAndroid Build Coastguard Worker 138*1c60b9acSAndroid Build Coastguard Worker``` 139*1c60b9acSAndroid Build Coastguard Worker if (!lws_json_simple_strcmp(buf, len, "\"src\":", "bc/user")) { 140*1c60b9acSAndroid Build Coastguard Worker if (!lws_json_simple_strcmp(buf, len, "\"event\":", "click")) { 141*1c60b9acSAndroid Build Coastguard Worker ... 142*1c60b9acSAndroid Build Coastguard Worker } 143*1c60b9acSAndroid Build Coastguard Worker ... 144*1c60b9acSAndroid Build Coastguard Worker } 145*1c60b9acSAndroid Build Coastguard Worker``` 146*1c60b9acSAndroid Build Coastguard Worker 147*1c60b9acSAndroid Build Coastguard Worker### Relationship between up / down and classification 148*1c60b9acSAndroid Build Coastguard Worker 149*1c60b9acSAndroid Build Coastguard WorkerClassification|Sequencing 150*1c60b9acSAndroid Build Coastguard Worker---|--- 151*1c60b9acSAndroid Build Coastguard Workerclick|down-up-click (it's classified when it went up and cannot be a longclick) 152*1c60b9acSAndroid Build Coastguard Workerlongclick|down-longclick-up (it's classified while still down) 153*1c60b9acSAndroid Build Coastguard Workerdoubleclick|down-up-down-doubleclick-up (classified as soon as second click down long enough) 154*1c60b9acSAndroid Build Coastguard Worker 155*1c60b9acSAndroid Build Coastguard WorkerIf the regime is configured for it, any "down" may be followed by one or more 156*1c60b9acSAndroid Build Coastguard Worker"stilldown" at intervals if the button is down long enough 157