Tuesday, August 18, 2015

Controlling Bluetooth Buttons

Since I mentioned in the last post that I was intercepting and modifying some of the buttons on the Bluetooth gamepad to respond to two different types of input, I thought I'd share the Tasker code that I'm using to do that.

Actually, most of the work is done by the AutoInput plugin, which is one of the AutoApps series that extends and simplifies Tasker itself.

Just as I did for the Digital Dash, I'll be using the gamepad as the input source.  Just to recap, here are the functions it sends by default:
 I'll just be capturing the "Media Fast Forward" button and giving it two functions: a normal click, and a long press.  Just for completeness though, I'm suppressing all the gamepad buttons.

The first thing to do is to run the AutoInput Key Suppress command:

KeySuppress (473)
A1: AutoInput Modes [ Configuration:Key Suppress: Enable
Keys: Enter
Back
Media Fast Forward
Volume Up
Volume Down
Media Next
Media Previous
Media Rewind Package:com.joaomgcd.autoinput Name:AutoInput Modes Timeout (Seconds):2 ] 

This function traps the specified key inputs and prevents them from being delivered to the OS.  If you don't do that, you can still make the keys do whatever you want, but they'll also be passed along and perform their original function as well.  That might be useful for some situations, like having Tasker's "Say" function confirm what key was pressed while still letting it through, but I want to completely change the key functions, so I'm going to suppress them.

In my Digital Dash project, the Key Suppress command is part of the Startup task, but you can just run it manually if you want to experiment, since it's persistent and will stay in force until explicitly cancelled. 

Now we need a couple of profiles and tasks.

Profile: FFLong (107)
Priority: 40
Event: AutoInput Key [ Configuration:Keys: Media Fast Forward
Key Action: Key Down ]
Enter: LongClick (106)
A1: Profile Status [ Name:FFShort Set:On ] 
A2: Wait [ MS:0 Seconds:1 Minutes:0 Hours:0 Days:0 ] 
A3: Profile Status [ Name:FFShort Set:Off ] 
A4: Flash [ Text:Long Click Long:On ] 

This is the profile that determines what happens when the key is pressed and held.

The first thing it does is enable the second profile (which we'll get to in a minute).  It then waits for one second and then deactivates the second profile.  After that, it simply performs the function you want.  Here's it's a simple Flash Alert, but it can be any set of Tasker actions.

The second profile determines what happens when you click and release the key before the one second Wait expires:

Profile: FFShort (108)
Priority: 45
Event: AutoInput Key [ Configuration:Keys: Media Fast Forward
Key Action: Key Up ]
Enter: ShortClick (103)
A1: Stop [ With Error:Off Task:LongClick ] 
A2: Flash [ Text:Short Click Long:On ] 

Note that this profile is activated when the key is RELEASED and the task it launches is at a higher priority than the one for "FFLong".

Because this task is at a higher priority, it can interrupt the FFLong task and stop it during the Wait period.   Beyond that time, the FFShort profile will become inactive and won't fire at all.

If it does activate it simply shuts down the FFLong task and executes its own action, again, in this case, a simple Flash Alert.

That's all there is to it.

A couple of things to note, however.  Firstly, you have to name a profile before you can set its status.  So, "FFShort" needed a name.  "FFLong" doesn't (technically) but it's always a good idea to name your profiles.  

Secondly, the actual status of "FFShort" doesn't matter after the key press has been handled.  If the "FFLong" task is the one that gets executed, the profile will be turned off.  However, if the "FFShort" task is called, the profile remains enabled.  But since it responds to a key up state and you can't have that without a corresponding key down event first, the profile can never trigger until "FFLong" is called first.

Of course, once you are done using the buttons, you need to stop capturing them.  This is done with the Key Suppress command again, but this time the configuration is set to disable:

KeyAllow (474)
A1: AutoInput Modes [ Configuration:Key Suppress: Disable Package:com.joaomgcd.autoinput Name:AutoInput Modes Timeout (Seconds):2 ]

Again, in this case, this is just a standalone task that you can run manually.  In my dash project that command is part of the Shutdown task.


Monday, August 17, 2015

More Tweaks and Tuning

Before and after our last roadtrip, I made a few changes to the Digital Dash system.  Nothing major, but I did address a few long-standing issues that have proven to be worth the effort to correct.

The first has to do with the Destinations panel.  If you've read some the previous entries, you know this is a pop-up panel that holds five preset destinations; all I have to do is pick one and the system starts navigating to it.

Normally it has an entry for Home, three places that we visit fairly often, and a slot for an ad hoc destination that's just called "Current".  Usually, I just edit the address for that last entry and don't bother changing the on-screen name.

However, for our last trip I needed to change everything except for the Home designation since we were going to be visiting several places in an unfamiliar area.  Changing the addresses was easy; all I needed to do was change the entry list in the "%V3_Destinations" variable.  However, to get the right names on the screen, I was going to have to manually edit the pop-up scene.  I wasn't too wild about doing that because I was just going to have to change it back after the trip and there's always the chance that I'd end up slightly moving the position of one of the elements.  And since the process that chooses the navigation destination relies on precise screen positioning, a small change would stop it from working.

So instead, I made a new variable in the Startup task: "%V3_DestinationNames" and turned it into an array.  It, of course, holds the onscreen names that I want to have displayed.  I also added a small loop in the Startup task that reads that array and uses Tasker's "Element Text" to change the names in the scene.  That way there's no risk of accidentally moving something and it makes future changes much easier to do.

I actually have two copies of both "%V3_Destinations" and "%V3_DestinationNames" in the Startup Task.  One holds my normal, default values and the other can be used for future trips.  I just disable the pair that I'm not using.

The second thing I did was go through and re-balance all the task priorities.  I admit I hadn't paid much attention to this before, even though I should have.  Before I did this, I would get occasions where the interface would be slow to respond; I'd hit the Next Track button and nothing would happen, so I'd hit it again and all of sudden it would jump two tracks.  Or it would take several seconds to pause the music.

To fix that, I went into every profile that dealt with user interaction and bumped the launched task priority up to around 40.  I also tweaked other tasks while I was at it, and the result is that the system is now very responsive to user input and there's never any confusion about whether the screen (or Bluetooth controller) tap has registered.

The last thing I did on the tablet was to fix an annoyance that has been around for quite some  time.  Every once in awhile the overlay scenes on Torque would "blink"; disappearing for a second before they would pop back on.  When this first started happening, long ago, I did a little investigation, but somehow came to the conclusion that it was something inherent in either Tasker or the Android OS.  After all, I was displaying four overlays, each with many elements and possible interactions and I figured I was just pushing the limits of the systems and it was something that I would have to live with.  And since was an annoyance rather than an actual problem, I just let it go.

But when I began to look at it a little deeper, I realized that it was actually my profile and task that was removing the displays.  I still don't really know why this is happening; the profile is triggered by Torque being in the foreground and none of my code is changing that condition when the problem manifests itself.  For some reason, Torque just loses focus for a fraction of a second from time to time, which was triggering the profile's exit task and removing the display, only to put it right back on again.

Fortunately, the fix was pretty simple.  I modified the exit task for the profile to start with a one-second Wait.  After that, it checks Tasker's built-in %PACTIVE variable to see if the profile is active.  If it is, the exit task is stopped.  This de-bouncing code is enough to stop the problem.  Since I put it in, the display has been rock-solid.

The final change I made was on the phone, rather than the tablet.  Again, if you read some of the earlier posts, you'll know that every couple of minutes the phone is supposed to read the temperature from its sensor and send it to the tablet via an AutoRemote message for display on the main screen.

The problem is that this has never worked quite right.  Messages wouldn't get sent consistently, or they'd get sent but never arrive, or a bunch of them would show up at once.  I ran lots of tests and tried lots of things, but I could never quite figure out the problem.

As it turns out, there were two issues:  One was that AutoRemote normally sends its messages through the web via Google.  That service seems to be a bit buggy and unreliable, causing lost and delayed messages.  The fix was to switch to using AutoRemote's Direct Messaging option.  Now, messages go from the phone to the tablet...uh, directly...using the Bluetooth tether between the two devices.

 That took care of messages the were sent but not received, but the system on the phone still wasn't sending messages consistently.  This time, the problem was of my own making.

When I decided to mount the phone in the car as well as the tablet, I did so because I wanted to have Waze constantly running and visible because I like its road hazard reporting.  However, I don't like its on-screen speedometer; it just too small to be useful.  So, I reused some code that I had originally developed to put a speedometer on Google Maps, without really paying much attention.

I had used a simple task that grabbed Tasker's %LOCSPD variable, converted it from meters-per-second to miles-per-hour and fed that to an onscreen variable.  The task would then wait before looping back and repeating.  The problem was that it was "waiting" exactly one millisecond.  My speed was being reported with great granularity, but that loop was blocking the low-level task I had set up to send the temperature messages.

Rather than rewrite this routine to use AutoLocation, I simply modified it by adding the temperature sending code to the loop.  Now, it has a loop counter that is incremented every time the speed is updated and after so many times, will send the temperature and reset the counter so it can start incrementing again.  I also changed the wait to 250 milliseconds, which is much more reasonable.  The result is that I now get consistent temperature updates about once per minute on the tablet.  It finally works the way I wanted.

None of these changes were very big, but they have made the system more stable, responsive, and user-friendly.

Thursday, August 06, 2015

Physical Controls for the Digital Dash



I know, of course, that it's somewhat dangerous (not to mention illegal in many places) to operate a touch screen device while driving.  That's why I've wanted to add physical controls to my system for quite a while.  In fact, the latest version of the system was built with that in mind, with a structure that was prepped for it.

I had initially become pretty excited about the Flic Bluetooth button when I heard about it last year on the Tasker Google Group.  I backed their project on Indiegogo and ended up ordering five of them.  I've become a bit disillusioned, though.  They were supposed to deliver in March, and it's now August and I still don't have them.

I had intended, originally, to mount four in the car: two on the center console for the passenger, and two on the back of the steering wheel (that mirrored the console buttons) for the driver.  I figured that with two buttons, I could control just about everything in my system.  I may still mount one on the console for passenger music control, but I've found something better for complete driver control.


This is a "Compact Bluetooth Gamepad" that I bought from GearBest.  It only costs about $7, has a rechargeable  battery, and incorporates nine buttons (including a four-way joystick) that send standard Android keycodes to your device.  And with Tasker and the AutoInput plugin, I can intercept those codes and make them do anything I want.  And what I want is to control the digital dash system...completely.

Here are the keycodes it sends in it's default mode.  There are at least two other modes that change the mapping, but I haven't messed with those.


I've remapped all the buttons except for the Play/Pause control; that one I use as is.  In addition, each of the joystick buttons uses two linked Tasker profiles to allow it to recognize two different types of activation: a short click, or a longer, held one. (I could do that with the buttons as well, but haven't needed to...yet.)



This picture shows how I've got it mounted in the car.  An inverted "U-shaped" piece of plastic with a couple of self-adhesive rubber feet to hold it in place and a couple of Velcro dots to hold the controller to it.  It gives me a very nice little sidearm controller that's easy to reach and use.  In the descriptions that follow, "up" is toward the front of the car.

Here's a list of functions that each button can perform, broken down by screen:

On the Main Screen

Joystick Up Click - Launch Google Maps
Joystick Up Held - Show the list of pre-programmed destinations
Joystick Down Click - Launch the MyRadar weather app
Joystick Down Held - Launch the Weather Channel app
Joystick Left Click - Replay previous song
Joystick Left Held - Show the list of favorite songs
Joystick Right Click - Skip to the next (random) song
Joystick Right Held - Launch PowerAmp if the music source variable is currently set to "Local".  If it's set to "Net", launch Pandora, instead
Volume Down Click - Launch Pandora and start playing my pre-existing 60's music station
Volume Up Click - Launch Pandora and start playing a mix of my pre-existing stations.
Back Key Click - Launch Pandora and start playing my pre-existing 70's Rock station
Enter Key Click - Launch GasBuddy

On Google Maps

Joystick Up Click - Return to Main Screen
Joystick Up Held - Toggle Voice Navigation on and off (uses AutoInput to press on-screen controls)
Joystick Down Click - Download A-GPS data using the GPSStatus app. (uses AutoInput to press on-screen controls.  Automatically returns to the main screen.)

On Radar or Weather Apps

Joystick Click Down - Return to Main Screen

On GasBuddy

Enter Key Click - Return to Main Screen

When the Destinations List is Showing

Joystick Down Click - Step the cursor down to highlight the next destination in the list.  If you try to step below the last one, it wraps around back to the top of the list.

Joystick Up Click - Step the cursor up to highlight the previous destination in the list.  If you try to step above the first one, it wraps around back to the bottom of the list.

Joystick Left Click - Cancel destination selection and remove the on-screen list

Joystick Right Click - Select a destination and send it to Google Maps.  Navigation starts automatically.

When the Favorite Songs List is Showing

Joystick Down Click - Step the cursor down to highlight the next song in the list.  If you try to step below the last one, it wraps around back to the top of the list.

Joystick Up Click - Step the cursor up to highlight the previous song in the list.  If you try to step above the first one, it wraps around back to the bottom of the list.

Joystick Left Click - Cancel song selection and remove the on-screen list

Joystick Right Click - Select a song and send it as a search to PowerAmp.  The song will start playing and then the system returns automatically to the main screen.

The Play/Pause key executes the Play/Pause function regardless of what screen is showing.

It seems a bit complicated when you're just reading through the list, but the operation is really pretty intuitive (at least to me.)  That's the beauty of designing your own system; you can have something that makes perfect sense to you, even if others don't see it the same way.

If you've read some of my previous entries, you might notice that there are a few functions that aren't accounted for.  They're used so rarely that I didn't feel it was necessary to add those in, but if I get bored, or ambitious I might do it anyway.  The missing functions are: launch ScannerRadio, set the PowerAmp EQ, toggle Auto-Brightness, and launch the fireplace app.

 I just got back from a long weekend mini-vacation where we put about 700 miles on the car and the system worked very well.  So far, I'm very pleased with how this part of the project has turned out.