Today I found out that you can plug in a regular HID keyboard into the Parrot Asteroid Smart and it will work. You know what else you can plug in that’s like an HID keyboard? An Arduino!! This means we can begin adding hardware controls to the head unit by mapping keyboard keys to software functions. This also means we can use the Arduino to wire up factory steering wheel controls of cars that are not compatible with Unika (Parrot’s steering wheel control interface).
So I’m setting off on a quest to program an Arduino to act as an HID device and to use this as a steering wheel adapter for my ’05 Pontiac GTO. The GTO uses a resistor ladder steering wheel interface, so this should be fairly easy to wire up.
In Part 1 of this tutorial, the goal is to get you to understand how to program the HID keyboard Arduino and at the end you should be able to plug it into your Asteroid Smart and navigate UP and DOWN.
HID Arduino
First things first, you need to turn your Arduino into an HID device. This part is actually fairly easy, though it may be time consuming. The first thing you need is to install a DFU Programmer. Instructions are on the Arduino site, however if you’re on a Mac and you’ve had MacPorts installed, but you’ve switched to Lion and now MacPorts don’t work, here are some tips.
ALSO!!!! If you just don’t feel like messing with MacPorts, or you hate it as much as I do , just skip this whole thing and compile the DFU Programmer source manually. It’s probably going to take less time.
Fixing MacPorts
Here’s the list of possible errors you may be getting truing to install the DFU Programmer with MacPorts:
Error: No valid Xcode installation is properly selected. Error: Please use xcode-select to select an Xcode installation: Warning: The Command Line Tools for Xcode don't appear to be installed; most ports will likely fail to build. Error: org.macports.configure for port dfu-programmer returned: can't read "configure.compiler": can't read "developer_dir": can't read "developer_dir": no such variable Please see the log file for port dfu-programmer for details: /opt/local/var/macports/logs/_opt_local_var_macports_sources_rsync.macports.org_release_ports_cross_dfu-programmer/dfu-programmer/main.log To report a bug, follow the instructions in the guide: http://guide.macports.org/#project.tickets Error: Processing of port dfu-programmer failed
The first thing you need to do is open Xcode, accept all the license agreements, then go to Preferences > Downloads and install the command line tools:
Then, run the following command to accept the Xcode license agreement:
sudo xcodebuild -license
Press “Q” to exit, then type in “Accept”.
Next, use “xcode-select” to point to the current Xcode installation:
sudo xcode-select -switch /Applications/Xcode.app/Contents/Developer/
Update to the latest version of MacPorts:
sudo port selfupdate
And finally, install the DFU Programmer:
sudo port install dfu-programmer
You should see something like this on your screen:
---> Computing dependencies for dfu-programmer ---> Configuring dfu-programmer ---> Building dfu-programmer ---> Staging dfu-programmer into destroot ---> Installing dfu-programmer @0.5.4_0 ---> Activating dfu-programmer @0.5.4_0 ---> Cleaning dfu-programmer ---> Updating database of binaries: 100.0% ---> Scanning binaries for linking errors: 100.0% ---> No broken files found.
Whew. That was fun wasn’t it? Oh who am I kidding, that sucked..
HID Firmware
You need to the HID firmware for your Arduino as well as the original firmware. I’m using the Uno R3 for this, by the way. Not sure if other models work, they probably do. You cannot upload a sketch to your Arduino when it’s in HID mode, so you’ll need to flash firmware between flashing sketches. Kind of annoying, but that’s life..
To flash the firmware, you need to put the Arduino into DFU mode. You do so by connecting the “Reset” and “GND” pins closest to the USB connector together:
Everything has to be hard on a Mac, you may need to do this additional step to download and build the latest version of the DFU programmer. If you’re getting the “dfu-programmer: no device present.” error, perform the following steps. Otherwise, skip over the compiling DFU Programmer source part.
Compiling DFU Programmer Source
Get the latest version tarball from here. Extract then compile it:
./bootstrap.sh ./configure sudo make sudo make install
If you used the method above, you want to cd into the “src” folder where the freshly built executable will be found. Run the following to verify you’re looking at the version you were trying to build:
$./dfu-programmer --version dfu-programmer 0.6.0
Flash Firmware
Now, attempt to flash regular firmware to your Uno R3. Short the two pins to enter DFU mode and run this:
./dfu-programmer atmega16u2 erase --debug 3 ./dfu-programmer atmega16u2 flash --debug 3 <path-to-firmware-file> ./dfu-programmer atmega16u2 reset --debug 3
Just for verification, here’s what it looked like on my screen:
$ ./dfu-programmer atmega16u2 erase --debug 3 target: atmega16u2 chip_id: 0x2fef vendor_id: 0x03eb command: erase quiet: false debug: 3 device_type: AVR ------ command specific below ------ validate: true $ ./dfu-programmer atmega16u2 flash --debug 3 /Users/Yuri\ A/Uno\ Firmware/Arduino-usbserial-uno.hex target: atmega16u2 chip_id: 0x2fef vendor_id: 0x03eb command: flash quiet: false debug: 3 device_type: AVR ------ command specific below ------ validate: true hex file: /Users/Yuri A/Uno Firmware/Arduino-usbserial-uno.hex Validating... 4058 bytes used (33.02%) $ ./dfu-programmer atmega16u2 reset --debug 3 target: atmega16u2 chip_id: 0x2fef vendor_id: 0x03eb command: reset quiet: false debug: 3 device_type: AVR ------ command specific below ------
Also during this time all sorts of cool LEDs lit up on the Uno. Ok, try burning some simple sketch. You might need to unplug the Arduino and plug it back in for the IDE to see it. If you succeeded you can now successfully burn the firmware!
Test HID Input
With the default firmware on your Arduino, load the following sketch.
uint8_t buffer[8] = {0}; boolean dirty = true; void setup() { Serial.begin(9600); // Set the button pins pinMode(8, INPUT); pinMode(9, INPUT); // Set internal pullups digitalWrite(8, HIGH); digitalWrite(9, HIGH); } void loop() { if (digitalRead(8) == LOW) { sendKey(0x51); // DOWN } else if (digitalRead(9) == LOW) { sendKey(0x52); // UP } delay(125); releaseKey(); } void sendKey(int code) { buffer[2] = code; Serial.write(buffer, 8); dirty = true; } void releaseKey() { if (dirty) { buffer[0] = 0; buffer[2] = 0; Serial.write(buffer, 8); dirty = false; } }
You’ll need to add two push buttons on pins 8 and 9 as in the picture below:
Now, flash the HID firmware:
./dfu-programmer atmega16u2 erase ./dfu-programmer atmega16u2 flash --debug 3 /Users/Yuri\ A/Uno\ Firmware/Arduino-keyboard-0.3.hex ./dfu-programmer atmega16u2 reset
No warnings here is good! Unplug your Arduino and plug it back into your computer. It should be detected as a keyboard. Test it on your computer to make sure you can press up and down 😀
Now go plug it into the Asteroid!!
Stay tuned for Part 2, where I will be attempting to add more controls and setting up a test rig (resistor ladder) to simulate my car’s steering wheel buttons.
Great work, Yuri! Did you have a chance to figure out key codes to control folder navigation in the default media player (up/down in folder list)?
-albertr
I haven’t had a change to mess with it yesterday, but I did manage to dig up a default HID key code mapping table which corresponds to what I have been seeing so far (with a few exceptions). I don’t think we’ll be able to control very specific features like folder up/down, but we should at least be able to adjust volume and navigate around any screen. Here’s the document that contains the keycode mappings: http://source.android.com/tech/input/keyboard-devices.html
It pretty much describes which HID keys will work and which won’t. Next step is to figure out how to map other keys to Tasker functions. I think I’ve got that one covered, just need some free time to test my theory.
Yuri, I’m not familiar with Tasker, but it looks like this tool should be able to inject keycodes, so it can be helpful when scripting some keycode sequences after starting up the application. However, I’m more interested in having the ability to intercept keycodes from a HID device and then inject different sequences of keycodes based on foreground application and it’s current activity (i.e. screen), for instance to have a single D-PAD to inject different sequences of keycodes to different applications depending on their activity. Not sure if it could be even possible… Do you know if there’s a way in Android to inquiry what is the active current screen (i.e. media player folder navigation dialog)? Please forgive me my terminology, I’m not an android gui programmer…
-albertr
Answering my own question.. Looks like there’s a way to figure it out along the lines below:
ActivityManager am = (ActivityManager) this.getSystemService(ACTIVITY_SERVICE);
// get the info from the currently running task
List taskInfo = am.getRunningTasks(1);
Log.d(“topActivity”, “CURRENT Activity ::”
+ taskInfo.get(0).topActivity.getClassName());
ComponentName componentInfo = taskInfo.get(0).topActivity;
ComponentInfo.getPackageName();
That at least gives some hope that it can be done from background service which listens for HID events and then injects appropriate keycodes based on current foreground application and its activity.
-albertr
Tasker can inject HID key events, but only if the device is rooted. Lucky, root is already available for the Asteroid (I’m in the process of writing a post on how to root and install google play store). What you’re suggesting are application specific macros that are executed when a system service detects a key press on an external USB HID device (Arduino) and depending on which app we’re in, executes the macro. I believe this is definitely doable on a rooted device, but may require Android-side coding. I’m going to try to first get generic HID stuff working (volume, skip, pause) then move on to try tackling this.
Tested the HID volume keys and they work. So we can for sure control the volume. I’m even thinking of installing a temporary (or possibly permanent) rotary knob somewhere. Dpad functions also work. I have a “mode” button on my steering wheel that I’m contemplating mapping to a software switch that’ll change the functions of the other 5 keys from media controls (skip/pause, volume, voice control) to d-pad (menu, direction, select). And maybe a third mode for switching between running apps. Also, if you have something like Utter! installed (highly recommend!!) then the voice control HID key works to bring that up, instead of Parrot’s voice control. Utter! can do all the same functions (contacts, music, etc) and more, like launching apps, navigation and search. There may be a way to set up Tasker to display a toast when switching modes (steering wheel button functions)
Hello Yuri! I apologize in advance for my English. I found a way to control the player without Tasker. Enough to edit the file \ system \ usr \ keylayout \ qwerty.kl
For example, assign a button F2 – source; F3-previous, next-F4. Fragment of this file will look like this:
key 60 SOURCE WAKE
key 62 MEDIA_NEXT WAKE
key 61 MEDIA_PREVIOUS WAKE
Thanks! That’s actually very useful as it will eliminate the delay between pressing the next/previous buttons on the Arduino steering wheel adapter and actually seeing the track change. Have you been able to re-map the keys and have them work with this method?
To remap the keys should have the root. Also need to remount the directory \system\usr\keylayout\ in read/write. I use Total Commander, adding a button to remount directories.
http://www.androidpolice.com/2011/07/25/tip-how-to-enable-writing-to-read-only-partitions-remount-rw-in-total-commander-for-android/
This method actually works much faster. If you know the Russian language, you can read my article on the forum (to register on the forum to open the link.)
http://4pda.ru/forum/index.php?showtopic=435503&st=2720#entry28738484
Спасибо за информацию! Нужно будет как-нибудь попробовать. Вы позволите перевести ваш пост на английский (со ссылкой)? Как-нибудь когда у меня появится время я бы хотел написать новый пост про то как можно подсоединить Asteroid к Arduino не используя Tasker для таких вещей как переключение трека.
Конечно переводите! Я также могу Вам выслать свой скетч для Arduino UNO, в котором я реализовал чтение кнопок руля для своей TOYOTA HILUX 2011 (в USA аналогичные рули у TACOMA и 4RUNNER) и чтение инфракрасного пульта дистанционного управления. За основу своего скетча взял Ваш код и вот эти статьи:
http://compcar.ru/forum/showthread.php?t=4640&p=57800&viewfull=1#post57800
http://compcar.ru/forum/showthread.php?t=4959
Кстати, я пробовал переназначение кнопок управления плеером как в стандарте на Android 4
key 163 MEDIA_NEXT WAKE
key 164 MEDIA_PLAY_PAUSE WAKE
key 165 MEDIA_PREVIOUS WAKE
и это должно было работать, подтверждение здесь http://4pda.ru/forum/index.php?showtopic=435503&st=2840
но к сожалению это у меня не работает вместе с Arduino, скорее всего проблема с прошивкой HID клавиатуры – очевидно большие коды клавиш не передаются – по крайней мере такие сообщения есть на сайте разработчика, а вот переназначение кнопок F2-F3 работает без проблем.
Спасибо! Как видите, у меня со временем не очень густо (с тех пор как вышел замуж). Так еще и не попробовал ваш способ, пока-что. Переведу как только будет возможность.
Большое спасибо за идею с /system/usr/keylayout/qwerty.kl. Поменял SEARCH+KEY_X из tasker на кнопки F2-F5. Всё работает замечательно. Теперь можно отправить tasker на заслуженную пенсию.
Рад что смог помочь!
I downloaded the Flip DFU programmer but there is not an option for a ATMEGA328. Do I need to buy an older Arduino?
I was using an Arduino Uno R3, if that helps.
Yuri I have some questions about adapting this to a project I’m working on. Don’t want to clutter this was wondering if we could chat by email.
Hi Yuri,
first thanks for your Asteroid Smart and your Arduino writeups. You motivated me to try something similar in my car. I was wondering if you ever wrote a Part 2 or 3 of the project?
Right now, I’m pretty close to making it work, but I’m issing a piece of the puzzle; the connection bewteen the Arduino and the Asteroid smart.
I’m able to use my arduino to detect key presses on my steering wheel controls (Lexus car).
I’m able to setup my arduino in HID-Usb mode.
I installed tasker to play with it. I still have a lot to learn with it but I see it’s incredible potential.
What I’m missing is how exactly do I need to send to commands to the Asteroid smart, and how do I catch those keycodes with Tasker. I was wondering if you could give me hints on how to find the solution. You can contact me by email too.
Thanks again, keep posting on your site!! it’s been a while I think! : P
Hey, it’s been a long time since I did the writeup. I think I never got around to posting how the key strokes are actually captured by Tasker. The trick is to send a “quick launch” key combination. I don’t remember exactly what it is, but if you look at Tasker it allows you to set up a keystroke (like Ctrl+H) and assign that to an action. You need to set these up and link them to actions, then send the keystrokes from the Arduino. Good luck!
howdy – i have the parrot asteroid smart and the need to control my music from a distance. i was inspired when i saw this video:
https://www.youtube.com/watch?v=2hLaYl8b08s
so i acquired the handheld device and rooted the unit as recommended. now i just need to know what keyboard command to program it with to make it control basic stop/start, vol up/down, forward/backward functions on the parrot asteroid smart.
some trial and error informed me that F12 pulls up the menu and the up/down arrows scroll likewise, but unfortunately do not control the volume like i’d hoped.
any insight so i don’t have do this the long way?
thanks!