Introduction
Communicating with an Arduino via Bluetooth is not new, but there are not many tutorials that describe how to do it. This is a precursor project aimed at adding Android programability and control to an Arduino; specifically, to be a prototype in an open sourced smart home control system where a user can change the channel on their TV, automate their water heater to save money, and monitor their thermostat, all from their Android phone.
Goals
This writeup's goal is to document the start-to-finish steps needed to connect to a Bluetooth-connected Arduino and issue IR commands to a TV. Its secondary goal is to add IR “learnability” where you can aim your remote at the Arduino and press a button to have the Arduino “remember” that IR flashing sequence. The third goal is to add cohesion with an app on the Android platform to use its bluetooth to command the Arduino to flash IR LEDs to control a TV.
Requirements
Hardware
- An Arduino
- A Breadboard
- Breadboard Wires
Bluetooth
- A BlueSMiRF Bluetooth Modem ( SFE WRL-00158 $64.95)
- A USB Bluetooth Dongle
- A Break Away Male Header, Right Angle (SFE PRT-00553 $1.95)
- A 2.4GHz “ducky” (or similar) Antenna with RP-SMA connection
Infrared
- OPTIONAL IR Receiver Module (RS 276-640 $4.19; PNA4602 & TSOP4838 may also work)
- IR LED (RS 276-143 $2.19; Others may work as well)
- A switching transistor if your IR LED consumes more than 20 mA
- A current-limiting resistor for the LED and resistors to setup bias on the OPTIONAL transistor
Software
- Ubuntu 11.04
- bluez-utils
- blueman-manager
- moserial
- Arduino IDE v.22
Setup
Hardware
A row of six (6) pins are broken off the 40-pin strip of the Break Away Male Header and soldered to the Bluetooth Modem so as the longer straight part of the header are pointing away from the antenna end of the Bluetooth Modem and are parallel with the board. The antenna is connected via the RP-SMA connector on the Bluetooth Modem. The bluetooth Modem can now simply plug into a breadboard so the Bluetooth Modem and antenna is perpindicular with the breadboard. The modem's pins should span 6 individual rows and not be connected in the orientation where all the pins are shorted. To be sure, use a multimeter (or similar) to test the resistance or connectivity of the individual pins; there should be infinite resistance between the pins, or, if using the continuity test, your multimeter must not “buzz” when testing the pins.
A breadboard wire is inserted to short (connect) the first and last pins on the modem; these are the “CTS-I” and “RTS-I” pins labeled on the modem. Next, wire the “VCC” pin to the “5V” pin on your Arduino. Now, this particular modem has over voltage protection built in; it is very possible that your modem cannot tolerate 5V and may use 3.3V. PLEASE read the datasheet on your modem to figure out what voltage it needs; even if you bought the exact same modem as above, there could be changes in the design. Next, wire up the “GND” pin from the Arduino to, guess what, the “GND” pin on the Arduino.
Now the UART communications (Tx/Rx pins) between the modem and Arduino are not intuitive for everyone. The “Tx” pin on the Arduino connect to the “Rx” pin on the modem and the “Rx” Arduino pin is connected to the “Tx” modem pin. Just make a mental note for the future that you should always double-check the wiring of the connections between devices as this may be a commonly mistakable issue when things don't work correctly.
Once everything is wired up, power up your Arduino by the USB port or barrel-socket (if your Arduino has one.) Check to make sure the green “PWR” LED is glowing and the modem is flashing its red “STAT” pin. My modem flashes at about 1Hz.
Wireless
Bluetooth
This section may need more clarification, testing, disambiguity, etc. as this is not a fool-proof method of connecting to the modem. I usually have to “fiddle” with things – which I will document – to get it connected; I have yet to determine the exact underlying cause. I would also like to get this step worked out in the terminal and get away from the GUI tools as they may be a cause of my symptoms. The tools needed are sdptool, hcitool, etc.
Open up “Bluetooth Manager” from »System »Preferences. Inside the application, click »Adapter »Search. Your modem should display after a short period; If after 60 seconds nothing appears to have happened, you'll need to go through the standard troubleshooting protocols (lsusb, dmesg, modprobe, google) to get it displayed. Right click the Bluetooth device that came up and select “Setup…”. The Bluetooth Assistant will popup and ask you for a pairing method. If you're using the modem listed above, select the second option: “Use Custom Passkey:”, enter “1234” as is listed in the modem's documentation and click the “Forward” button.
In my case, this is where the trouble happens when it states it cannot connect to the device. I've seen some success with merely trying again. Sometimes I need to remove the device, search for it, and going through the setup again. Other times, I need to “sudo /etc/init.d/bluetooth restart” and go through the steps again.
When you finally get it connected, it will be bound to ”/dev/rfcomm0” or something similar. Open up moserial and configure it with the default connection settings as put forth in the documentation:
- Device: /dev/rfcomm0
- Baud Rate: 115200
- Data Bits: 8
- Stop Bits: 1
- Parity: None
- Handshake: Hardware CHECKED; Software UNCHECKED
- Access Mode: Read and Write
- Local Echo: UNCHECKED
Click the connect button, enter “$$$” into the “Outgoing” textbox (which puts the modem into command mode), and hit enter. You should get “CMD” as your response and the red “STAT” LED on the modem will be blinking at about 10Hz. From here, take a look at the myriad of commands in the data sheet available to you. Here's a few of the more useful commands in order you would encounter them:
- $$$ – Enter CMD Mode
- - - - (3 dashes, no spaces) – Exits CMD Mode
- H – Help
- D – Displays basic settings
- E – Displays extended settings
- SF,1 – Set the device to its factory defaults
- R,1 – Reboot
- SN,<name> – Rename your modem
- ST,<0-255> – Sets the time window (starting at bootup) in which you may be able to enter into CMD mode; 0=disabled, 255=forever
- U,<rate>,<pol> – Temp baud rate change; e.g. U,9600,N
- SU,<rate> – Changes the baudrate; use first 2 numbers of 4800,9600,115K,921K, et al.
- P,<text> – Pushes <text> to UART
- T,<0,1> – Toggles UART passthrough while in CMD mode
Infrared
Most of the Arduino + IR control pages on the net are useless. I found ladyada's TV-B-Gone design notes to be a great help in understanding IR protocols and efficiently storing them on an ATiny. Also, Ken Sheriff's blog and Arduino 'IRremote' library are indispensible; he's also written about his way to control a stereo with his phone or web browser by using an intermediary HTTP server to send the IR codes to the Arduino1).
To begin controlling devices such as TVs and stereos with the Arduino, you need an IR LED setup on one of the pins on the Arduino. I'm using a 100 mA IR LED and since the Arduino can only source 20 mA, I'll want to connect a switching transistor in-line with the LED. Such transistors are the 2N2222, 2N2907, 2N3904, 2N3906, etc. In the mean time, I'll just connect the anode of the LED to an Arduino pin and the cathode to ground. This will suffice but is not optimal.
For simplicity, I used Ken Sheriff's IRremote library and his examples, but it just records IR signals and plays them back. I'd like to have the Arduino just playback the IR commands I send to it via serial. A few ways to do this: create a binary protocol, use a communications protocol already developed like Firmata, or simply write commands along with an argument (IR code) for the Arduino to process. I chose the latter to get this project done as fast as possible, but now I have to take ASCII codes from Serial.read() and convert them to hexadecimal. Easy! Here's a function I threw together:
// Should really only return an unsigned nibble byte getHex(char ascii){ byte code = (ascii & 0x0F); // Drops the left nibble in the ASCII character if ((0x41 <= ascii) && (ascii <= 0x46)) // {A-F} code += 0x09; // {A-F} -> {0xA-0xF} else if (!((0x30 <= ascii) && (ascii <= 0x39))) // {0-9} -> {0x0-0x9} return -1; // Returns 0xFF if not hex return code; //Returns {0x0-0xF} }
The rest of the serial code looks like this:
String out; void setup() { Serial.begin(9600); } void loop() { int nBytes = Serial.available(); if (nBytes > 0) { for (int i = 0; i < nBytes; i++) { out += (char)Serial.read(); } if (out.endsWith("\n")) { // Wait until '\n' Serial.println(); Serial.print((String)out.length() + " bytes read; "); Serial.print(out); parse(out); out = ""; } } } void parse(String cmd){ if (cmd.startsWith("n")){ String str = cmd.substring(cmd.indexOf(" ") + 1, cmd.length() - 1).toUpperCase(); unsigned int len = str.length(); unsigned long ir=0L; #ifdef DEBUG Serial.print("str: "); Serial.println(str); Serial.print("len: "); Serial.println(len, DEC); Serial.println(); #endif for (int i = (len-1); i > -1; i--) { char ascii = str.charAt(i); byte code = getHex(ascii); unsigned int j = len-i-1; ir += (code<<(j*4)); #ifdef DEBUG Serial.print("i: "); Serial.print(i); Serial.print("; char: "); Serial.print(ascii); Serial.print("; ascii code: "); Serial.print(ascii, HEX); Serial.print("; hex: "); Serial.print(code, HEX); Serial.print("; ir: "); Serial.println(ir, HEX); #endif } Serial.println(); Serial.print("IR Cmd:"); Serial.println(ir, HEX); } }
(more to come)