Thursday, June 29, 2017

A simpler Amazon Dash trigger implementation for Google AIY

As I mentioned in my previous post there's an easier way to get the Amazon Dash trigger working with your Google AIY kit.
On the Raspberry Pi forums poster sheridat was kind enough to direct me to another implementation of Dash Trigger controls which doesn't require any modification to the Google AIY kit code.

Using this trick you should be able to create a trigger based on anything using any programming language that supports Raspberry Pi GPIO. It comes down to this:
The default trigger (on button press) can be tricked by toggling the GPIO pin it's attached to

So in Python the code to trigger the AIY kit can be done with either the following long-form script or it's shorter brother:
With this code in hand it becomes more straightforward to get the dash button trigger working. The Raspberry Pi forum post doesn't quite detail the process so I'll do so here. Much of this is copied over from my previous post.

First you'll need to ensure you have scapy and tcpdump available to root. To do so open a terminal and run the following commands: You'll also need to configure your dash button to connect to your wifi but not make any purchases. There's good writeup in the MagPi post, but I'll also provide a short breakdown here:
  1. Configure your Amazon Dash button completely with the Amazon app
  2. Deactivate the button
  3. Start configuring the button again, but when you reach the "choose an item" page don't select an item and exit out of the app.
This will leave your device capable of connecting to wifi but not making a purchase when you press it. When you press the button you should see white blinking lights followed by red lights (indicating a purchase was not completed). If you see a green light you should check your Amazon account in case you need to cancel the order.

The next thing you need is the MAC address(es) of your dash button(s). Once again the instructions exist in other examples, but I'll provide the short summary here. You'll need the MAC address of your Dash button(s).

If you don't know what a MAC address is you might have some trouble. The only pointer I can give is that a MAC address is 12 hexadecimal digits grouped into pairs separated by colons e.g. 1A:2B:3C:4D:5E:6F.

You should be able to log in to your router and get the IP and MAC address of your dash button(s). On my router it showed up only by the MAC address, however I've read that it may show up as "Internet Device". It's important to know that the Dash button usually connects about 5 seconds after it is pressed, and that it may not show up on your router except immediately after it connects.

Note: it's been reported that you can shorten the response time to 1 second by configuring a static IP address for the dash button with your router.

Warning: I've found that the phone I used to configure the Dash button is constantly pushing notifications whenever the Dash button is pressed. For me this is acceptable because I don't need notifications from the Amazon app and can block them. If you normally use the Amazon app this could be a problem I don't currently have a solution for.

The next big step is to create a script that will trigger whenever the dash button is pressed.

For reference you should probably try a debugging script. Here's the python script that helped me understand what was wrong with my configuration (it's based on a StackExchange query with some good sample code): Note you'll need to invoke the above script with sudo.
Note I've found that you need to have the Raspberry Pi plugged into ethernet. Wi-Fi may not work to detect ARP packets. You should see a listing of ARP requests until you exit using ctrl+c. If you've hit the Dash button in this time its MAC address should show in the listings.
 
Now here's the script to use your dash button(s) as a trigger:
  • You'll need to replace ##:##:##:##:##:## with your MAC address(es).
  • If you changed the trigger file in the previous examples you must configure it here by setting the variable "trigger_file" (default is "/tmp/voice_recognizer/trigger" same as our trigger script above).
  • Each dash button MAC address needs to be added to the "buttons" dictionary. The keys (i.e. "Bounty" or "Charmin") need to be unique for each dictionary entry, but you can add as many unique dictionary entries as you like.
  • It has to be run as root to work.

Getting the script to run whenever you want On my system I saved the above script as "/root/src/detect_dash.py" (you'll probably need to "sudo mkdir /root/src" before you can save there) I'll use this location in the following examples. You'll need to make this script executable with "sudo chmod +x /root/src/detect_dash.py".

Finally let's get the script running whenever you want the button to be detected. You have a few options:
  • By far the easiest is if you can open a terminal and leave it running. All you need to do is run and you should see that the "/etc/voice_recognizer/trigger" file is touched after each button press. 
  • If you are running from ssh or otherwise running without a desktop environment you can familiarize yourself with screen or tmux in order to get it running in the background. Unfortunately a full explanation of how to use those programs is outside the scope of this post.
  • Another way to run it from ssh is to force it to the background (you won't get any debugging messages if there's a problem!). To run the script in the background type into a terminal This complicated line will run the script in the background, hide the outputs and prevent the script from stopping when you disconnect.
  • If you want it to run every time your system starts up you can use the root crontab to schedule it. To edit the root crontab enter "sudo crontab -e" in the command prompt and add a new line to the crontab with the text "@reboot sudo python3 /root/src/detect_dash.py". Reboot your system and see if it worked!
Thanks for reading! I hope this helped someone.
Feel free to leave questions in the comments section or on the Raspberry Pi forums

Monday, May 29, 2017

Triggers for Google AIY (Including Amazon Dash button!)

Edit June 29, 2017: Hi folks from the MagPi! If you're looking for a simpler implementation of the dash button trigger see my next post! If you're looking for a more in-depth review of the AIY trigger system that will all stay here. I hope you enjoy!

I was one of the few lucky folks to get my hands on a Google AIY voice kit for Raspberry Pi from The MagPi. After following the official instructions and toying around with the box I really wanted to figure out how to implement some additional features.

My eventual goal was to implement the "Ok Google" hotword detection, but it seems I was beaten to the punch.
But of course learning is a big part of the fun, and the Google documentation for the Trigger system was lacking, so I kept hacking at my first set of test code and learned a few things worth sharing.

First off I'll present the code that I finally got working, take it a step further with Amazon Dash button detection, and finally summarize some important notes that seem to be lacking from the trigger documentation.

Getting a trigger to work:
I'll save the detailed commentary for the code summary at the end. As a first attempt I put together an example Trigger definition that triggers voice recognition when a certain file is created on the filesystem. Create the folowing file at "~/voice-recognizer-raspi/src/triggers/file.py": The code above has a few notes worth knowing:
  • When the file specified by TRIGGER_FILE (default is "/tmp/voice_recognizer/trigger") is created the system will trigger voice recognition and delete the file to reset the state.
  • By default the trigger's folder is configured as world-writeable. If you want to implement secure access (e.g. by ACL or user/group) you should change the TRIGGER_FILE to a persistent folder on the disk (e.g. "/home/pi/voice_recognizer_raspi/src/trigger/trigger_file/trigger") and set the folder (i.e. "trigger_file/") permissions accordingly.
The next step is to update the main.py script so that it knows about the new trigger.
You need to modify "~/voice-recognizer-raspi/src/main.py" and change the section pertaining to triggers from this: to the following: Now you should be able to invoke the AIY voice recognition from the command line by feeding the main.py script with the flag for the file trigger: In another command prompt you can test voice recognition with the following code: That should cause the voice recognizer service to turn on (the light should turn solid) and wait for your prompt. Once it has responded to your prompt the script will re-arm for the next time the file is created.

The really nice thing about this configuration is it makes programmatic triggering much easier than writing a trigger script; to trigger the voice kit all you need to do is have your program create the trigger file. This should make it relatively easy to write a trigger that will fire off for arbitrary functions such as a webpage submission, pressing the GPIO button on the kit, or even an Amazon Dash button. Unlike the other included triggers it can even trigger for multiple types of input without making modifications to the voice recognizer code.

Triggering Voice Recognition with an Amazon Dash button:
So far I have been able to adapt the Amazon Dash button example code from the MagPi to trigger voice recognition.
The bad news is the example is missing some important details, but the good news is I seem to have worked them out on my Pi and I'll share what I know here.

I'm going to show how to implement this in python3 which I'm most familiar with, but for those more familiar with Java there are examples using Node.js that you may find easier to follow. I didn't use Python 2.7 because I was having problems getting scapy to run.
First off you'll need to configure your environment with the necessary packages. The example I'm working from depends on a Python library called scapy, which also depends on the package tcpdump.

Important note: scapy sniffs packets, which requires low-level access to the network interface. These scripts will only work when run as root (e.g. with su, sudo, or the root crontab). It's generally a bad practice to run code you do not trust with root privileges: it's up to you whether you think the benefits outweigh the risks. If for some reason you suspect the following source code, python, scapy, or tcpdump could harm your system you should be aware that these tasks will have the most privileged access to your Raspberry Pi. I cannot be held responsible for any ill effects caused by executing the following code.

Now then, first you'll need to ensure you have scapy and tcpdump available to root. To do so open a terminal and run the following commands: You'll also need to configure your dash button to connect to your wifi but not make any purchases. There's good writeup in the MagPi post, but I'll also provide a short breakdown here:
  1. Configure your Amazon Dash button completely with the Amazon app
  2. Deactivate the button
  3. Start configuring the button again, but when you reach the "choose an item" page don't select an item and exit out of the app.
This will leave your device capable of connecting to wifi but not making a purchase when you press it. When you press the button you should see white blinking lights followed by red lights (indicating a purchase was not completed). If you see a green light you should check your Amazon account in case you need to cancel the order.

The next thing you need is the MAC address(es) of your dash button(s). Once again the instructions exist in other examples, but I'll provide the short summary here. You'll need the MAC address of your Dash button(s).

If you don't know what a MAC address is you might have some trouble. The only pointer I can give is that a MAC address is 12 hexadecimal digits grouped into pairs separated by colons e.g. 1A:2B:3C:4D:5E:6F.

You should be able to log in to your router and get the IP and MAC address of your dash button(s). On my router it showed up only by the MAC address, however I've read that it may show up as "Internet Device". It's important to know that the Dash button usually connects about 5 seconds after it is pressed, and that it may not show up on your router except immediately after it connects.

Note: it's been reported that you can shorten the response time to 1 second by configuring a static IP address for the dash button with your router.

Warning: I've found that the phone I used to configure the Dash button is constantly pushing notifications whenever the Dash button is pressed. For me this is acceptable because I don't need notifications from the Amazon app and can block them. If you normally use the Amazon app this could be a problem I don't currently have a solution for.

The next big step is to create a script that will trigger whenever the dash button is pressed.

For reference you should probably try a debugging script. Here's the python script that helped me understand what was wrong with my configuration (it's based on a StackExchange query with some good sample code): Note you'll need to invoke the above script with sudo.
Note I've found that you need to have the Raspberry Pi plugged into ethernet. Wi-Fi may not work to detect ARP packets. You should see a listing of ARP requests until you exit using ctrl+c. If you've hit the Dash button in this time its MAC address should show in the listings.
 
Now here's the script to use your dash button(s) as a trigger: Notes:
  • You'll need to replace ##:##:##:##:##:## with your MAC address(es).
  • If you changed the trigger file in the previous examples you must configure it here by setting the variable "trigger_file" (default is "/tmp/voice_recognizer/trigger" same as our trigger script above).
  • Each dash button MAC address needs to be added to the "buttons" dictionary. The keys (i.e. "Bounty" or "Charmin") need to be unique for each dictionary entry, but you can add as many unique dictionary entries as you like.
  • It has to be run as root to work.

Getting the script to run whenever you want On my system I saved the above script as "/root/src/detect_dash.py" (you'll probably need to "sudo mkdir /root/src" before you can save there) I'll use this location in the following examples. You'll need to make this script executable with "sudo chmod +x /root/src/detect_dash.py".

Finally let's get the script running whenever you want the button to be detected. You have a few options:
  • By far the easiest is if you can open a terminal and leave it running. All you need to do is run and you should see that the "/etc/voice_recognizer/trigger" file is touched after each button press. 
  • If you are running from ssh or otherwise running without a desktop environment you can familiarize yourself with screen or tmux in order to get it running in the background. Unfortunately a full explanation of how to use those programs is outside the scope of this post.
  • Another way to run it from ssh is to force it to the background (you won't get any debugging messages if there's a problem!). To run the script in the background type into a terminal This complicated line will run the script in the background, hide the outputs and prevent the script from stopping when you disconnect.
  • If you want it to run every time your system starts up you can use the root crontab to schedule it. To edit the root crontab enter "sudo crontab -e" in the command prompt and add a new line to the crontab with the text "@reboot sudo python3 /root/src/detect_dash.py". Reboot your system and see if it worked!

Finally we've reached the part where I clarify what was learned about the Trigger system of the Google AIY kit.
Triggers don't have any particular documentation. The getting started guide directs you to the GPIO trigger code (which is undocumented) and expects you to figure out the rest. Here's what I've figured out about how triggers work:
  1. The "main.py" script kicks off everything. To implement a new trigger you need to make it aware of the trigger you'd like to use. In the code example above we added the lines that tell it what flag to accept for the trigger, what file and class to import, and what text to prompt when the system initializes the trigger.
  2. From the trigger class definition (file.py) the system will run the start() function and wait for the trigger to make a callback (self.callback()).
  3. When the callback occurs the system listens for a prompt, and responds.
  4. After the system has responded it will return to step 2, retriggering the start() function.
I did run into a few walls setting this up. The examples provided by Google don't mention anything about threading because they are invoking libraries (recorder / GPIO) that perform threading for them. I found that when I ran my callback from within the start() thread the system would hang up; apparently main.py invokes the trigger class in the same thread. To fix this I had to use the following syntax to make my triggering function threaded: That's pretty much all I needed to make sure it got going. It's also worth noting that having your thread sleep saves CPU usage.


Thanks for reading! I hope this helped someone.
Feel free to leave questions in the comments section or on the Raspberry Pi forums

Sunday, January 13, 2013

Kodak Wi-Fi Firmware Update

If you're like me you bought a Kodak wireless printer thinking "Wow! This'll be great! No cables for all my printing and scanning needs!" only to find out that the wireless feature absolutely does not work. Kodak's printers are fine, but their software is absurdly bad.
The problem is that on the stock firmware any time the printer goes to sleep (a maximum of 4hrs idle) it will lose all connectivity with your PC.

So needless to say I was excited to see that the latest firmware from Kodak includes "Improved Wi-Fi stability." I clicked "update firmware" and when I saw the words "update complete" I was relieved. I was so very mistaken.

My printer behaved exactly the same, except now it would notify me I needed a firmware update every day.

"A firmware update is available for your printer
Current Version: 2.2
Latest Version: 4.4"

It was constantly telling me to update my firmware from 2.2 to 4.4. If I told it to update it would tell me it had updated successfully, and then shortly after tell me I was running firmware 2.2.

So I dug around and was able to find the following help page: Printer firmware version stays the same after an upgrade
As it turns out Kodak just never fully supported 64-bit Windows. If you are using a Windows PC less than 5 years old the firmware updates will not work. So I had to follow the workaround on that page.
Still, the Kodak directions make it seem easier than it is, which is why I'll post the correct details here:

*note: if you have access to a Mac you may find the Mac firmware update process (ESPC310/ESPC315) to be more straightforward. The following steps should work regardless of your operating system.
  1. Backup and format all the contents of an SD card.
  2. Download the latest Kodak firmware for your printer from the "mobile devices" page (ESPC310/ESPC315)
  3. Save the firmware (ESPC31xA.rbn) as the only file on your SD card. If you did not format the card the printer may not allow a firmware update.
  4. Safely remove the SD card from your PC.
  5. Insert the SD card into your printer.
  6. Dismiss the "No viewable pictures found on memory card." by either pressing OK or waiting for it to disappear.
  7. Hold down the OK button and while it is held down press the Back button.
  8. Follow the on-screen directions to begin a firmware update. Once the update has begun do not remove the SD card or turn off power to the printer until the update is completely finished.
The printer display should tell you the firmware is updating, then when it is done it will restart and open the default menu. At this point it is safe to remove your SD card.

If everything was successful you should be able to use the Kodak AiO home software on your PC to consistently connect to your printer wirelessly and be able to configure Google Cloud Print.

You can verify your firmware version by using the printer's menus to go to Maintenance -> Print Test Page. If the update was successful it will print on line five "05. Firmware Version: . . . RC4.4"

Hope this helps somebody. I know I was struggling with this for over a year.

Disclaimer: with any project to modify firmware there is a very real risk of leaving your device in an unusable state. No one should follow these directions if they are uncomfortable with the process. It is highly recommended that you put in the time to research the solution before embarking on this project and understand each step presented. The reader accepts full responsibility for any consequences of following this tutorial.

References:
Printer firmware version stays the same after an upgrade
Kodak AiO Home Software (required for wireless features)