Archive for May, 2012
I've done some more work on the app. It is now possible to configure the user interface by way of an XML file. At the moment modifying the XML file requires building the app again but this will be changed so the XML file can be downloaded and uploaded from within iTunes.
The XML file specifies a number of "pages" (in the screenshots I have two pages: "TV" and "Channels"). The user swipes sideways to navigate between the pages.
Each page has a layout that specifies how large the buttons are and where they are positioned. Right now the app only supports "medium buttons" (3 x 4 buttons) and "large buttons" (3 x 3 buttons) but more will be added.
Each button position on a page can be left empty or hold a button. A button is configured with an ID (the command ID used – thus the same command can be sent from more than one button), a color (which sets the button's background image), a text and a mode. Mode is either "normal" (command is sent on touch up), "touchdown" (command is sent on touch down) or "repeat" (command is continously being sent as long as the button is pressed).
View a sample configuration file here.
Screenshots – click to see full screenshot:
The PCB for the BLE multi-remote is done and it looks like everything fits. Except for the 10 µF capacitor (left side, middle) where I've used a C instead of B sized footprint. This is why you should always print out a 1-to-1 sized version of the PCB and place the actual components on it (never mind the values, of course, as long as the correct packages are used):
Oh, and if you can't find the TSOP34838 IR receiver anywhere then that's because I forgot to include it in the schematic and board… I just realized that. Doh!
Anyway, it's been added where C7 (bottom-middle) is now and C7 has been moved a bit to the left and up and rotated 90°.
I've run into a few problems with the AVR firmware. (The GitHub repository for which is located here.) Recording of IR signals works fine and the values I record correspond nicely to the theoretical reality ("theoretical reality"?). If you can't remember the protocols and timings, you can find a great explanation of the various protocols here.
But – when I send the signals they are too long (as recorded by my oscilloscope connected to the output pin of the MCU which controls the IR LEDs).
My first approach was:
_delay_us( high_duration );
_delay_us( low_duration );
First I tried simply subtracting a "trim" value from each recorded duration. Which almost worked. But the necessary trim value became so high that, for short durations, the total delay became less than zero microseconds (which, of course, was interpreted as a really high number instead). And even if I clamped the delay values so they couldn't be less than zero, the pulses were still too long.
So instead I now use the 16 bit TIMER1 (TIMER0 is used to generate the 38 kHz output to modulate the IR signal). I configure the timer for CTC mode with the same duration as when sampling IR signals. When learning signals I increase a counter (16 bit resolution) to record the pulse as a number of "sample intervals".
Now when sending, I simply use the raw recorded counter value and decrease a counter on every compare match of the timer. When reaching zero, I toggle the IR signal and reset the counter to the next value in the recorded sequence. When I reach a counter value of zero I stop the timer and the signal is complete.
And a whole 'nother thing is that I've been running the MCU on 3.3 V at 16 MHz. Which according to the datasheet (section 28.3 Speed Grades) is out of spec. Sure, it may well run fine but in this case where timing is crucial I had better throttle it down to 12 MHz which is inside the Safe Operating Area.
And lo and behold – everything works!
The first revision of the PCB layout for the multi-remote project is done. Mind you, I always go through a couple of revisions before getting it manufactured (because I always mess something up in the first few attempts).
I've made a couple of upgrades to the iPhone app: it now has four command buttons as well as "Toggle debug window", "Record IR" and "Scan for devices" buttons. The command buttons and the Record button are only enabled when a device is connected. And the Scan button is only visible (replaces the Record button) when no device is connecte
The app now starts searching automatically when becoming active. If it discovers the last device it was connected to, it connects immediately. Otherwise, it shows a list of devices and lets the user pick a device to connect to. Only devices that advertise the correct service UUID are listed.
The UI also got a little upgrade – it's still just a prototype, mind you...
Also, I've added a MOSFET (IRL1404 – for the PCB version I'll use a IRLML2502) to drive the IR LED with more power. Before, the LED was just driven directly from a MCU pin (which, as everyone knows, is bad form). Now, the MCU pin is connected to the gate of the MOSFET (logic-level) which turns on the LED and current limiting resistor for 100 mA in a common-source configuration.
As expected, the range improved tremendously. And I'm still only using one wide-angle LED.
I have gotten a prototype assembled and working. So far, I've got the following working:
- Connect and disconnect to devices advertising a specific service UUID
- Automatically connect if a known device UUID is found
- Read RESPONSE attribute (although it is not used for anything at the moment)
- Receive notifications when the RESPONSE attribute is updated
- Send commands by updating the COMMAND attribute
- Send commands to the MCU over serial USART when the COMMAND attribute is updated. (Only the command type is sent now – the sequence ID is not yet included.)
- Commands are also sent to the MCU when a client device connects or disconnects (which is ignored at the moment).
- Record an IR signal and store it in the EEPROM.
- Playback the IR signal stored in EEPROM (or a hardcoded test sequence).
- Write the stored IR signal to serial for debugging.
- Respond to commands received on the serial line and trigger recording, playback or one of the debugging commands.
And it works! I can switch my television on and off from my iPhone :)
Next step is to use both high-intensity and wide-angle IR LEDS and to drive them at much higher power (right now the one wide-angle LED is being driven directly by the MCU – which you should never do).
Here is a picture of the prototype in all its glory:
And here is another picture with the individual parts labelled:
Here's some notes on how to store IR codes for the IR controller project:
If I were using pre-programmed codes in a few known formats (like RC-5 or NEC1) I could store the commands using just the two or three bytes that actually make up the sequence. For example, a NEC sequence consists of one address byte and one command byte (although it is sent as a lead-in followed by four bytes).
But mostly I will need to record IR signals and store those. The proper way of doing it would probably be to parse the recorded signal, identify the protocol and store the required few bytes. But that sounds like way too much work. So instead I'll just be storing the time intervals for on-off pairs. I would like to use one byte for each time code and if I use 100 µs resolution I will get from 0.1 ms to 25.5 ms in .1 ms intervals. I think that might be good enough – but time will tell :)
Firstly, an ATmega328P has only 1 KB of EEPROM storage. And most likely, that will not be enough. So I'll be using an external EEPROM chip. The "24C" series seems to be very widely used and uses a simple I2C protocol. So something like Microchip's 25LC512 seems suitable (512 Kbits, 3.3 V and is available in both DIP and SOIC packages) and will give me an extra 64 KB of non-volatile memory. I'm thinking that will be enough.
The IR codes will have differing lengths. And the correct way of storing those in the EEPROM would involve some kind of allocation table and stuff in order not to waste memory space. But instead of messing around with all that I've decided to go for a super-simple solution there every IR code uses a fixed size (like 128 bytes – something that is a whole multiple of the EEPROM chip's page size). That way I can easily access the code for a specific index.
The data will be in raw time-on, time-off format and terminated by a 0 value (since a 0 ms pulse will never occur).
Thus, for my LG television which uses the NEC1 protocol, an "off" command (address 0x04, command 0xC5) can be stored like this:
06, 06, 06, 06, 06, 17, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06,
06, 17, 06, 17, 06, 06, 06, 17, 06, 17, 06, 17, 06, 17, 06, 17,
06, 17, 06, 06, 06, 17, 06, 06, 06, 06, 06, 06, 06, 17, 06, 17,
06, 06, 06, 17, 06, 06, 06, 17, 06, 17, 06, 17, 06, 06, 06, 06, 00
For a total of 67 bytes for a code that basically consists of two bytes! Way inefficient, I know, but it's easy to use.
Using a size of 128 bytes for one IR code will accomodate codes of up to 64 IR pulses and I will be able to fit 512 codes into the EEPROM – and that should be more buttons that I'll ever need. (Maybe I will need to up the size to 256 bytes – we'll see.)
I've made some progress on the BLE112 module. So far, I have:
- Got it wired up in the most basic configurations (VDD, GND, debugging and RESET and TX/RX for UART1, alternative 1 and UART0, alternative 2).
- Got the CC Debugger working.
- Created a project consisting of a hardware setup specification, a simple GATT specification and a BGScript.
- Created an iPhone app that scans for Bluetooth LE devices with the required service UUID, connects to a peripheral and finds the two required attributes (one for sending commands to the BLE112 and one for reading responses).
So far, so good. But I've run into a small problem: I can send serial data from the BLE112 (in response to a startup event or when an attribute changes its value – like when the iOS app writes a value to the command attribute) but I can't seem to receive serial data on the BLE112. Everything should be configured correctly (I think) but the event handler simply doesn't get called in the BGScript.
Maybe the port needs to configured differently. Or maybe I will give up and try using SPI instead and see if that helps.
However, since the BLE112 is used only to receive commands from the iPhone and pass them on to the MCU (like "send IR code such-and-such"), I may not even need to send data from the MCU to the BLE112...
I'll do some more troubleshooting and see if I find a solution.
(It seems I'm not the only one with this problem.)