Sunday, November 15, 2015

How to make your Android Phone Really Wake You Up!

This project requires a some hardware. Despite this being a software blog, I figured we could mix the two together eventually. Let this be the first cross over into metal! Huzzah!

Basically, because I work better at night than I do during the day, I had trouble waking up for my 8:00AM class. I also had this LED light strip hanging over my bed. Not only did this thing have 16 fantastic colors on it, but it also had another set of LEDs that were bright white. Like, really really bright white. I figured I could get these to wake me up! So I set out on a mission to get my phone alarm to trigger something to make these bright beasts turn on to get me out of bed.

The easiest bridge I could think of (seeing as my phone, the Nexus 4, doesn't have an IR Diode on it) was an Arduino. I have experience with DFRobot's Blunonano and I love it. It's about $35 on their website (which is where I got mine) which isn't bad considering the regular Arduino Nano is $30 from this seller on Amazon. Of course, you can use any Arduino you want with some sort of Bluetooth Shield, but I think it's easiest to use the Blunonano. That's what this tutorial is written for.


You'll also need an IR LED, an IR Receiver, and a 75Ω resistor. I don't know much about electrical math, so I tried a 100Ω resistor first to be on the safe side. This resulted in the LED not being bright enough to make it across my bed. 75Ω seems to work fine, although you may be able to go lower than that. I picked those three things up from my electronics store for about $2. You'll also probably need two short wires. I probably didn't wire mine in the most optimized way possible, but it works and, being as I haven't done much with the Arduino in the first place, that's all I can ask for. You'll also need a breadboard. It doesn't have to be as large as mine. The one I use just happens to be the one I had on hand.

This is the final product. I know it's ugly.

When wiring the Arduino, make sure it isn't plugged in. That said, you're going to want to wire the Arduino's +5v pin to the far left power rail. I know my breadboard says negative on it, but it doesn't matter. I put the ground on the other power rail. I proceeded to put the two power pins of the IR Receiver right into the power rails. Here's the pin-out specification for the IR Receiver.


I made the mistake of putting it in backwards and it started to smell and get super duper hot. I was surprised to see it still working after I practically made it run backwards. You put the data out into any digital port except for #3. Note this number for later.

Next, I put one end of the resistor into digital pin #3. This is the default used by the IR Library. Next, I put the other end of the resistor into some other row not attached to the Arduino and put the shorter end of the IR LED into that row. I then put the longer end of the IR LED into the ground. I thought it was interesting that the longer end is negative on IR LEDs which is opposite of the LED standard.

That's it for the hardware set up! Congrats! Now to program the board. The following assumes you have the Arduino IDE installed and working.

First, you're going to need the IR Library. Or maybe you don't. Arduino says they fixed the IDE which had broken support, but I used the newest version and it was still broken. So I just installed this library and it worked fine because Arduino will chose this library over the one that's shipped with the IDE during compilation.

The first thing you'll want to do is learn the codes of your remote. This depends on the kind of remote you have. Upload the IRrecvDumpV2 project to the board after changing the variable recvPin to the number of the pin the receiver is in. After that, open the Serial Monitor from the Tools menu and set the baud rate to 9600. Take aim with a remote and fire. You should see some output from your Arduino about the signal it just received!


These are the codes from Volume Up and Volume Down on my Vizio TV. Here we only need to take note of three fields. You need the Encoding and the Code and the length (32 bits). This is what is required to send the data to the device.

Next, we're going to make the LED send some stuff. Take the code you want to use and the encoding, and we're going to send it like this:
irsend.sendNEC(0x20DF40BF, 32);
This is pretty much the syntax for all of the other encodings as well. If you have a JVC device, you may need to take additional steps, although I'm not quite sure what they are. Check the example code provided with the library for more details on how to this. Anyway, the function goes hex code, then code length. Here's some test code I wrote for my light set to turn them on and off every two seconds. This is modified from one of the examples:
/*
   IRremote: IRsendDemo - demonstrates sending IR codes with IRsend
   An IR LED must be connected to Arduino PWM pin 3.
   Version 0.1 July, 2009
   Copyright 2009 Ken Shirriff
   http://arcfn.com
*/

#include <IRremote.h>

IRsend irsend;

void setup()
{
  Serial.begin(9600);
}

void loop() {
  while(true) {
    irsend.sendNEC(0xFFA05F, 32);
    Serial.println("Sent a thing!");
    delay(2000);
  }
}
You can verify this is working by looking through your phone camera and seeing it light up against a dark background. You obviously can't see this with your naked eye. Next, we'll send data over serial to trigger the light. To do this, modify your look like such:
void loop() {
  if (Serial.available() > 0) {
    Serial.read();   
    irsend.sendNEC(0xFFA05F, 32);
    Serial.println("Sent a thing!");
  }
}
This should do it for the code on the Arduino! Get excited!

Get the base code from the DFRobot's repository and import it into your favorite IDE. Moving the code into Android Studio was tedious but overall took about 5 minutes. With the Arduino unplugged from the computer and plugged in to some other power source, Then fire the app up. It should look like this:

Click "Scan" with your Bluetooth turned on (the app will ask you to do this if it's not) and your Arduino turned on. It should show up as something to do with "Bluno". Tap it to connect. You can then type data and send it to the Blunonano over the air which is pretty spectacular if you ask me.

Now, we need to integrate it with the Android Desk Clock. This is where it gets hairy, because each phone is different. I was blessed with a phone that runs vanilla Android, so I'm going to write this for the stock Android clock.

In order to see what kind of broadcasts the Desk Clock sends off, we need to dive into the source code. I looked around and it turns out that the broadcast emitted by the clock is defined here as "com.android.deskclock.ALARM_ALERT". which is super convenient for us. Except for the part where I have no idea how to keep a connection to the Arduino while the phone is sleeping. If anybody out there knows how to do this, please let me know because this is clearly the best way to do this. So we're going to need another approach that requires the phone to only connect once.

I decided to with a timer setup on the Arduino to see how accurate it would be. I'm sure the internal timer would keep time very accurately over short intervals. So I decided to try this over very long intervals.

I want this app to be able to do multiple different things, so I wrote a simple protocol for commands and arguments. This is what I came up with:

  • *10 will turn the lights on in 10 milliseconds from command process.
  • @ is the literal command. This is a direct translation from a number to a hex code to send over the air for making the phone an actual remote.
You can expand upon this, but this is what I'll be writing the code for. Here's the base code for the * command.
void loop() {
  while (Serial.available()) {
    delay(3);
    if (Serial.available()>0) {
      char c = Serial.read();
      readString += c;
    } 
  }
  if (readString.length()>0) {
    if(readString.charAt(0)=='*') {
      long millis = atol(readString.substring(1).c_str());
      long seconds = millis/1000;
      Serial.println(seconds);
      int mod = millis%1000;
      while(seconds!=0) {
        delay(1000);
        seconds--;
      }
      delay(mod);
      irsend.sendNEC(0xFFA05F, 32);
      Serial.println("I did it!");
    } else {
      Serial.println("Illegal Command");
    }
  }
  readString = "";
}
I tested this for the command *60000 and it waited for exactly one minute. To test it for longer time, I wrote the app to power it based on the sample app.

To do this, I threw in a button, a time picking dialog fragment and a little bit of sketchy math to approximate the time the lights should turn on. I think there's a more accurate way to do this, but I first wrote the prototype at 3AM on a Mountain Dew energy rampage. So my mind isn't exactly the clearest. I'll revisit the calculation when I know that this one is at least close. Based on the numbers I know that it's doing it somewhat correct, but this along with multiple one second delays may not bode well for its accuracy. Luckily I didn't have to wake up on time the morning I was testing it.

The synchronization worked like a charm. No time was lost over the course of the night and the processor worked fast enough on the decrement operator as well as the comparison of the long to not gain extra seconds. I'm sure if this were to run over the course of a year it would lose quite a bit of accuracy.

I haven't implemented the @ command yet, but I feel like this blog post has dragged on for long enough. Implementing the full remote should be rather trivial. If something breaks or I find a better way to do this, I'll write a follow up to this.

The code for this can be found here. I apologize for the embarrassing spelling error.  I was typing fast and by the time I noticed it it was too late. Again, I didn't write this to any quality standard so it may be a bit messy. I threw this together because I needed to force myself to wake up at the correct time sooner rather than later.

No comments:

Post a Comment