Project-CT

MythTV Remote Control System
Senior Project Final Report
Design Team 2
Thomas Cramer
Brent Gamble
James Williamson Jr.
Dr. Joan Carletta, Faculty Advisor
Submitted: April 22, 2011
Table of Contents
Reference Figures and Tables
List of Figures
i
List of Tables
i
Abstract
1.
1
Problem Statement
Need
1
Background
1
Objective
2
Research Survey
2
Marketing Requirements
3
Objective Tree
3
2.
Design Requirements Specification
3.
Accepted Technical Design
Design Overview
4
6
3a. Hardware Design
Hardware Theory of Operation
7
Level 0 Block Diagram
13
Level 0 Functional Requirement Tables
13
Level 1 Block Diagram
14
Level 1 Functional Requirement Tables
14
Level 2 Block Diagram
16
Level 2 Functional Requirement Tables
16
PCB Design and Application
18
Receiver Design and Application
22
Design Considerations and Calculations
23
Simulations
27
3b. Software Design
Software Theory of Operation
Remote
34
Receiver
37
Linux USB Driver
38
MythTV Driver
41
Installation
42
Level 0 Block Diagram
42
Level 0 Functional Requirement Tables
43
Level 1 Block Diagram
45
Level 1 Functional Requirement Tables
46
Level 2 Block Diagram
49
Level 2 Functional Requirement Tables
49
4.
Operation, Maintenance and Repair
5.
Testing Procedures
55
Remote Software
57
Receiver Software
60
Linux USB Driver
61
MythTV Driver
63
Installation Package
65
Remote Transmission Range
66
Network Application
67
Remote Power
68
6.
Financial Budget
69
7.
Project Schedule
71
8.
Design Team Information
75
9.
Conclusions and Recommendations
76
10. References
77
11. Appendices
78
List of Figures
Figure 1: MythTV Remote Control System Objective Tree
Figure 2: Remote Control Pushbutton Circuit Schematic
Figure 3: Example Diagram of Switch Bounce and a Simple Debounce Circuit[1]
Figure 4: Remote Control Transmitter Circuit Schematic
Figure 5: Standard AA Battery Form Factor
Figure 6: Receiver Circuit Schematic
Figure 7: Level 0 Hardware Block Diagram
Figure 8: Level 1 Hardware Block Diagram
Figure 9: Level 2 Hardware Block Diagram
Figure 10: PCB Layer 1 Rendering (Top)
Figure 11: PCB Layer 2 Rendering (Bottom)
Figure 12: PCB Top Photograph
Figure 13: PCB Bottom Photograph
Figure 14: Remote Internal Photograph
Figure 15: Assembled Remote Photograph
Figure 16: Assembled Receiver Photograph
Figure 17: Simulation of Remote Transmitter
Figure 18: Waveform Depicting PIC Output Pin Voltage Ripple Effect on LED Current
Figure 19: Waveform Depicting Source Voltage Ripple on LED Current
Figure 20: Example PIC24 Output Voltage Pulse Train with Base and Emitter Current Waveforms
Figure 21: Schematic of a Simplified Single-Column/Row Remote Pushbutton Circuit
Figure 22: Pushbutton Circuit Voltage and Current Waveform
Figure 23: Project Implementation of the RC-5 Protocol
Figure 24: Manchester Coding as Per IEEE 802.3
Figure 25: UML Diagram of Linux Software Package
Figure 26: Level 0 Software Block Diagram
Figure 27: Level 1 Software Block Diagram
Figure 28: Level 2 Software Block Diagram
Figure 29: Pulse width of 15ms on PIN 6, or row 2 on the remote.
Figure 30: The same row at repeated polling cycles with delay of 150ms.
Figure 31: Raw output for ROW 2, COLUMN 3.
Figure 32: Remote LUT for button codes
Figure 33: Decoded output for ROW 2, COLUMN 3.
Figure 34: Receiver input versus remote output.
Figure 35: Sending a command 0x81 to the receiver to fetch the latest message from the remote.
Figure 36: Data returned from the receiver hardware, in this case no new command.
Figure 37: Python Test Program
Figure 38: Method to test the functionality of the MythTV Driver
Figure 39: Linux USB Driver (mythremoted) is registered and starts when the computer turns on
Figure 40: Linux USB Driver (mythremoted) is currently running.
Figure 41: LED transmission as viewed through a cellphone camera
Figure 42: MythGUI network application interface
Figure 43: Graphical depiction of Table ABOVE (y-axis voltage, x-axis time)
Figure 44: Remote Button Layout
3
8
9
10
11
12
13
14
16
18
19
19
20
20
21
22
27
29
30
31
32
33
36
36
40
42
45
49
57
58
58
59
59
60
62
63
64
65
65
66
67
67
68
78
List of Tables
Table 1: Design Requirements Specification Table
Table 2: List of Required MythTV Remote Control Buttons
Table 3: Remote Control Unit FR Table
Table 4: USB Receiver FR Table
Table 5: Remote Microprocessor (PIC24FJ64GB002) FR Table
Table 6: Remote Control Transmitter FR Table
Table 7: Remote Control Power Source FR Table
Table 8: Remote Control Buttons FR Table
Table 9: Receiver Microprocessor (PIC24FJ64GB002) FR Table
Table 10: Receiver Infrared ICR Component FR Table
Table 11: Remote Pushbutton Array FR Table
i
4
5
13
14
14
15
15
15
15
15
16
Table 12: Remote NPN BJT FR Table
Table 13: Remote IR LED FR Table
Table 14: Remote 1.5V AA Batteries FR Table
Table 15: Remote Transmitter Simulation Legend
Table 16: Poll For Buttons() FR Table
Table 17: Send Data() FR Table
Table 18: Receive Data() FR Table
Table 19: Receive USB() FR Table
Table 20: Send USB() FR Table
Table 21: MythTV RC USB Driver Package() FR Table
Table 22: pollButtons() FR Table
Table 23: Input Capture on Columns FR Table
Table 24: LUT (Look-up table) FR Table
Table 25: messageGenerate() FR Table
Table 26: Receiver Input Capture FR Table
Table 27: ProcessIO() FR Table
Table 28: Linux USB Driver FR Table
Table 29: MythTV Driver FR Table
Table 30: Poll Rows() FR Table
Table 31: Input Capture on Columns (L2) FR Table
Table 32: LUT (Look-up table)(L2) FR Table
Table 33: messageGenerate() FR Table
Table 34: Timer (13.1us) FR Table
Table 35: Receiver Input Capture (L2) FR Table
Table 36: Receiver Timer (1.6ms) FR Table
Table 37: Good Message FR Table
Table 38: ProcessIO() (L2) FR Table
Table 39: Linux USB Driver FR Table
Table 40: connectToDevice() FR Table
Table 41: send(0x81) FR Table
Table 42: receive() FR Table
Table 43: sendToMyth() FR Table
Table 44: MythTVDriver (L2) FR Table
Table 45: MythTVDriver.py FR Table
Table 46: Voltage drain on remote battery in non-transmitting state
Table 47: Original Budget
Table 48: Revised (Final) Budget
Table 49: Original Gantt Chart Table
Table 50: Original Gantt Chart Graph
Table 51: Revised Gantt Chart Table
Table 52: Revised Gantt Chart Graph
Table 53: Remote Command Mappings
Table 54: PCB Trace Width Calculation Table
ii
17
17
17
27
43
43
44
44
45
45
46
46
47
47
47
48
48
48
49
50
50
51
51
51
52
52
52
53
53
53
53
54
54
54
68
69
70
71
72
73
74
79
80
Abstract
Author: James Williamson
The goal of this project is to design and develop a remote controller and a simple plugand-play USB receiver targeted exclusively for operation with MythTV. An open-source
application authored for the Linux operating system, MythTV emulates the function of a
digital video recorder (DVR) on a PC. Other universal remote options currently exist, but
they are often specific to tuner card peripherals, interface using outdated PC ports, or
prove difficult to configure and use for the casual consumer. Within this design, the
remote will be akin to that of a modern video recording system, with functionality
specific to operation of MythTV. Design emphasis will be placed on easy setup and a
shallow requisite learning curve. Using the proposed system will essentially allow the
user to make full use of hardware they already own to record television programs. In this
way, they can avoid a hefty monthly subscription fee for proprietary hardware and
service.
Key Features:
 Convenient and functional MythTV remote controller
 Compact USB receiver with plug-and-play capability
 Easily configurable software
1. Problem Statement
Need
Author: James Williamson
There are a variety of tuner cards for the home PC on the market. Depending on cost and
model, the cards may come with a remote. However, such remotes are almost always
exclusive in function to their respective hardware. The MythTV software comes with no
such device and instead relies on traditional methods of input like the keyboard and
mouse. Thus, a need has evolved for a universal and easy-to-use remote for MythTV; this
is primarily due to existing remote control devices bring homemade, cumbersome,
difficult to configure, or missing buttons that map directly to features within the
application.
Background
Author: Brent Gamble
Since the inception of television technology, consumers have long desired a way to store
their favorite broadcasts for the sake of convenience or replay value. Videocassette
recorder (VCR) devices originally fulfilled this need, but with the recent advances of
digital television equipment into the home (and the general inconvenience of
videocassettes), a new digital device became necessary. The digital video recorder is the
modern digital equivalent of a VCR. Most broadcast providers offer their own DVR
services to consumers with a few third party options, such as TiVO. Typically, such
1
functionality includes internal storage for recordings in addition to the ability to display
listings from the television provider. Most tech-savvy individuals are aware of how much
the television provider inflates prices for this service and have devised other means of
providing the same functionality. Enter MythTV; a free, open-source software suite that
turns a personal computer into a DVR. It provides a cost-effective alternative because the
hardware in the vast majority of modern computers is capable of the same operation.
Rather than pay for the DVR itself and a high monthly subscription fee for service, why
not simply purchase a tuner card for a computer and use free software to perform the
same functionality? Using traditional input methods for a PC are not optimal, however,
and prove undesirable in comparison to the comfort and familiarity a remote control
offers.
Objective
Author: Brent Gamble
The goal of this design is to develop an easy-to-use remote control and USB receiver
targeted exclusively for the MythTV software. It should provide all of the functionality
required to operate and navigate through the software and video screens while being
intuitive, easy to handle, and simple to install. In this manner, it should be easy to
configure and setup on any PC running the MythTV software.
Research Survey
Author: Thomas Cramer
There is a substantial market for viewing video on personal computers. Input sources are
typically a DVD player, digital camcorder, or live broadcast. Each solution provided by a
manufacturer usually includes software and a remote control for the system. As such, the
remote control device is catered specifically to the needs and user interface provided by
the software. Most are proprietary solutions with drivers and software developed for
Windows, and to a lesser extent, Macintosh. Drivers for these operating systems are not
compatible with Linux, which is the primary target for MythTV.
A remote control is essentially a transmitter that communicates with a receiver (most
notably by using infrared light) in order to allow a user to wirelessly interface with a
device. There is an open-source effort to provide support for receivers on the Linux
operating system called LIRC (http://www.lirc.org/). Originally designed to provide
support for a homemade or hobbyist receiver, it has now grown to support the many
proprietary devices shipped with the commercial remote controls. However, it only
supports serial and parallel receivers, and cannot share its port with another device. Serial
and parallel ports are becoming increasingly less common on computers today, and thus,
there is another project (http://tusb3210.sourceforge.net/) for USB receivers currently in
development. Both projects provide a driver to the Linux operating system, which is then
used to control applications and interface with the X Windows Environment.
2
Our goal is to provide an infrared remote control with a USB receiver. There are many
remote controls that work with LIRC and MythTV; however, the remote control
hardware implementation is either focused on a different software package or geared as a
universal solution. The current open-source driver implementations would probably work
with the hardware we design, but the software that utilizes the driver does not provide
ability to control MythTV in the manner desired. Additionally, our design requirement is
ease of installation which is currently not the case with the majority of existing solutions.
Marketing Requirements
Author: James Williamson
The device should be an infrared remote control with a USB receiver, and should support
the Linux operating system using a custom driver. These components should provide
control of the MythTV application, enabling complete and simple navigation of all
software menus. The hardware should be aesthetically similar to other remote control
solutions. This system should be easy to install with little to no additional configuration.
Objective Tree
Designer: Thomas Cramer
Products
Figure 1: MythTV Remote Control System Objective Tree
Package
MythTV Remote
Control Package
Implementation
Services
USB Receiver
(.60)
Remote Control
Transmitter (.40)
Encoder
Infrared
Communication
User Input
3
Decoder
Software Interface
Infrared Signal
Processing
Driver
2. Design Requirements Specification
Authors: Brent Gamble, Thomas Cramer, James Williamson
Marketing Requirements:
1.
The system should provide a complete remote control solution for the MythTV
application.
No external voltage source should be required for the remote unit.
The receiver should connect to the computer via the Universal Serial Bus (USB).
The remote should be able to transmit to the receiver across a typical living room
distance without failure of operation.
Battery life in the remote should be comparable to similar devices.
The remote form factor should be no larger than that of a typical TV/DVR remote.
The receiving unit should be comparable in size to a compact external hard drive.
The bundled software should be easy to install, and should require no user
configuration to make the system work with MythTV.
2.
3.
4.
5.
6.
7.
8.
Table 1: Design Requirements Specification Table
Marketing Requirements
1
2, 5
3, 7
5
1, 4
1, 6
1, 7
8
[1]
Engineering Requirements
The system should consist of a
remote control, USB receiver,
and Linux PC software.
The remote should draw
power from 2 AA batteries at
3V.
Receiver power will come
from the Universal Serial Bus
5V DC pin.
When idle for 10 seconds, the
remote should enter a lowpower state, and should
resume without delay to the
user upon continued operation.
The remote should be able to
transmit to the receiver at up
to 30 feet[1].
Dimensions of the remote
should not exceed 1" x 2.5" x
8."
Dimensions of the receiver
should not exceed 1" x 3" x 4."
The bundled software should
be a shell script common to
most Linux distributions.
Justification
Should replicate the operation
of existing commercial DVR
packages.
The PIC24 in this design has
an operating range of 2 - 3.6V.
Receiver power capitalizes
upon USB power, designed
for devices of this nature.
Battery life is the primary
constraint for the electronics
inside the remote, but should
not come at a performance
cost.
The remote should remain
functional in a typical
household living room.
Should be aesthetically
pleasing and compact, while
remaining functional.
Remote should be lightweight
and easy to use.
The user should not have to
know anything about software
or any specific commands to
the remote to use it.
Ideal maximum: this range can be adversely affected in highly fluorescent environments.
4
Table 2: List of Required MythTV Remote Control Buttons
MythTV Remote Control Buttons
























Numpad 0-9
Arrow Keys (U, D, L, R)
Exit (cancel)
Enter (accept)
Application Power/standby
System power/standby
Channel up and down
Volume up and down
Play
Pause
Stop
Rewind (successive taps for 2x, nx)
Fast Forward (successive taps for 2x, nx)
Mute
Chapter forward/back
Play DVD
MythTV menu
Schedule menu
Record
Goto watch TV
Input (change video inputs)
Program guide
Info (show channel details)
Underscore (necessary for some digital channels)
Total: 39
5
3. Accepted Technical Design
Design Overview
Author: James Williamson
The system will consist of a remote, USB receiver, and software package. In order to
communicate and process data, both the remote and receiver utilize embedded
microcontrollers (Microchip PIC24 models). Once the data is USB-ready, it is fed into a
Linux PC and processed by drivers in the software package to control MythTV.
The focal points of the design can be essentially broken down into 5 steps: interpretation
and encoding of selected commands (buttons), communication between two PIC24
microcontrollers, processing of received commands into USB-ready data, writing the
driver to read incoming USB data for use by the MythTV driver, and finally, writing said
MythTV driver to use sent commands to directly control the application. With this in
mind, it is important to gain an understanding of which design aspects will be
implemented in hardware as opposed to those implemented in software.
On the remote, the 39 buttons will be set up on a 4-row by 10-column circuit that the
PIC24 will continuously poll. When a button is pressed, it will register at which row and
column this occurred, and send an appropriate encoded message for that command. Thus,
the encoding has been relegated to software rather than hardware. Doing the design in
this way will not only save space on the remote due to fewer components used, but it also
plays to the strengths of the team by reducing hardware complexity and increasing the
programming that must be done on the microcontroller. The PIC24 will modulate the
appropriate signal with a carrier and output to the base of a bipolar junction transistor.
Amplification is done using this BJT, which will respond to the signal and drive an
amplified current through the infrared LED to facilitate message transmission. Power on
the remote is provided by two AA batteries in series, totaling 3 volts. Hence, the major
elements of the remote consist of a pushbutton PCB grid, software button message
encoding, and a transmitter composed of a BJT driving an IR LED.
The receiver unit is simpler still; it consists of an IR receiver integrated circuit connected
to the complementary PIC24 that will read the commands transmitted over the infrared
spectrum. When the PIC receives a command burst, it will prepare this data for transfer
into the PC over the universal serial bus. Power is provided directly by the bus on the 5volt line, resulting in a very straightforward circuit. The only required adjustment is a
regulator on the PIC source pin that converts the 5 volts to 3, to suit the operating range.
In essence, the design is composed of an IR receiver and the software message
processing. Once the data is fed into the USB port, the design is obviously completely
based in software for the remainder. A driver will be written for the Linux operating
system to interpret incoming data using the user-space LibUSB routines, which in turn,
will be supplied to the MythTV driver. It is within this software module that the original
button message will be treated like standard IO (such as a keyboard) and will directly
control the program.
6
3a. Hardware Design
Hardware Theory of Operation
Author: James Williamson
All hardware required for complete operation of this design can be fundamentally
distributed into one of two circuits: the receiver or the transmitter (remote control).
The remote control allows the user to command MythTV wirelessly. The first step is to
record and process which action the user wishes to take. In order to encode a button press
for transmission, steps must be taken to ascertain which button is activated. There are 39
different buttons (commands) issuable from the remote control. Thus, a 4-column, 10row electrical grid should be constructed to meet all possibilities. Figure 2 on the
following page depicts the schematic of the pushbutton circuit.
In this circuit, each row (R0 – R9) is polled by the PIC24 one at a time by applying 3
volts. Switches normally open will not complete a circuit, and the PIC will register 0V at
each column (C0 - C3). If, however, a switch is pressed, a circuit is completed at that row
and whatever column corresponds to the pushbutton. The current flows to ground at the
appropriate column, and the PIC registers a voltage greater than zero at the column
output. Thus, a specific row/column combination represents a unique button. The
resistors are placed to limit current flowing to the I/O pins, and are purely precautionary.
Only one row is polled at a time, so in the event that multiple buttons in that row are
pressed, there is a mechanical race condition of sorts that is influenced by manual user
interaction time to determine which button is registered first. Additionally, because the
design is electromechanical, the polling speed must account for switch bounce. When a
mechanical button or switch is physically activated from a user, the electrical contact can
bounce, causing transients in the switch output (see figure 3 on page 9). Bouncing refers
to the pulsed current that can result from the momentum and relative elasticity of the
contacts that are forced together. In the case of fast logic devices, turning the current on
and off quickly can have adverse effects on the circuit. If the switch is bouncing and
resides at a logic low when the polling is active, a button press could be neglected if the
speed is too fast and a new row is read too soon.
The pushbutton grid was one of the factors that influenced the selection of the
PIC24FJ64GB002 as the microcontroller best suited to this design. For the number of I/O
pins required, care was taken to select a device that could accommodate the design and
input voltage, while being cost-effective, easy to learn, and practical. The implementation
of PIC to PIC communication in the receiver and remote was also chosen because project
leader Brent Gamble and software manager Thomas Cramer utilized PIC
microcontrollers and the aforementioned phase of the design as their Embedded Systems
Interfacing term project. The requirement for this project was 4 weeks worth of
development that could be applied to the senior design course. Thus, it was deemed
practical to choose a platform that would not require the knowledge of a new language or
device.
7
Figure 2: Remote Control Pushbutton Circuit Schematic
8
Figure 3: Example Diagram of Switch Bounce and a Simple Debounce Circuit[1]
This circuit is not adversely affected by switch bounce because of its nature. On the PIC,
input capture hardware is utilized so when the column signal becomes a “1”, the change
is registered and bouncing would not affect quality of information.
The pushbutton grid in Figure 2 registers user input, and the PIC24 utilizes software to
both encode the command and modulate the output signal. In order to facilitate
transmission, the signal should now be amplified. This is accomplished via the circuit in
Figure 4 on page 10, which utilizes an NPN bipolar junction transistor. The NPN BJT
"turns on" and operates in the forward active mode when the base voltage is high relative
to the emitter voltage (and the collector is high relative to the base). This means that a
proportionally larger current will flow from the rail, driving the LED when the PIC
output is high. The 2N4401 NPN transistor was chosen because it is a good generalpurpose transistor suited to linear amplification and fast switching, capable of a
maximum collector current of 600mA; a value above what is demanded of this design
(the maximum PIC I/O pin output is 25mA and the TSAL6200 IR LED can handle
400mA peak steady current). Conversely, when the PIC output is low, the BJT will enter
the cutoff region, as the base voltage will drop below that of the collector and emitter. In
cutoff, there is almost no current flow from the collector to emitter.
9
The resistors in Figure 4 are used to (R1) control base current and to (R2) protect the
LED from damage (values are discussed and justified in the simulation section).
Figure 4: Remote Control Transmitter Circuit Schematic
The PIC output signal is modulated at 38kHz in accordance with the IR receiver IC
utilized in the receiver unit. Modulation is critical to helping the IR receiver component
ignore unwanted light and noise, primarily the terms from incandescent or fluorescent
lighting. It should be noted that the device will perform with a higher range in conditions
not saturated in fluorescent light, as per the absolute maximums for the IR LED and
receiver. The realistic conditions will vary and will be shorter depending on the
environment.
10
The entire remote is run on two 1.5-volt AA batteries in
series. The 3-volt approach was chosen because the
input voltage range for the PIC24 is 2 - 3.6 volts.
Additionally, the milliamp-hour rating for a AA battery
(around 2700mAH for a standard alkaline) is superior to
that of a 9-volt battery (around 660mAH); the original
choice for the project. This also simplifies the circuit in
that a regulator is no longer required in the remote –
both the PIC24 and transmitter will run off the 3V
source rail.
Once the remote is powered, input is gathered and
encoded, and the appropriate signal is modulated and
amplified, the message is ready for transmission over the
infrared spectrum.
Figure 5: Standard AA Battery
Form Factor
Infrared communication is ideal for remote control devices of this nature because it is
cost-effective and the signal will not interfere with devices in other rooms or areas as it
cannot permeate opaque objects. Because a user must often see the device they are
controlling (such as displays), this is not a setback as it would be for automatic data
transfer like wi-fi networks. The IR receiver/LED utilized in the design has a maximum
range specification of 35 meters at peak current, which is above satisfactory range for the
requirements specification of 30 feet. Additionally, infrared light is not visible to the
human eye, and will provide no distraction or annoyance when communicating in light or
dark areas.
Figure 6 depicts the receiver schematic. Data received from the TSOP1738 is fed to
another PIC24 that processes each button press and prepares the data for USB transfer to
the PC. The signal from the 1738 is an active low 5mA current. Thus, the PIC software
accounts for the fact that the transmission will be inverted.
In terms of power, the TSOP in the receiver runs on 5 volts whereas the PIC24 requires 3
volts. Thus, a regulator is placed on the input to the PIC, appropriate for a 5-to-3 volt DCDC conversion, which can deliver a maximum output current of 250mA (the maximum
allowable source current for the PIC as per the datasheet).
11
Figure 6: Receiver Circuit Schematic
In Figure 6, C1 is a decoupling capacitance value placed on the rails to smooth
fluctuations. This value consists of a 4.7 micro-, 10 micro-, and 100 nano-farad
combination of capacitors in parallel to target specific frequencies (relatively common
values for the purpose). Decoupling capacitors will be used on the regulator input and
output as well (not pictured). USB 2.0 specifications provide 5V of power with up to 5
100mA “unit loads.” Any device that exceeds these needs will need to provide an external
power source. Low power devices are classified as utilizing 1 unit load, whereas high
power devices may use all 5. Specifics on this matter for the receiver circuit are discussed
in the calculations section for the hardware.
12
Level 0 Block Diagram
Designers: Brent Gamble, James Williamson
The highest-level view of the hardware can be summarized by Figure 7 below. The
design consists of the remote control unit, USB receiver, and will integrate with a
personal computer on the software end.
Figure 7: Level 0 Hardware Block Diagram
User Input
Power
Power
Remote
Modulated
Data
USB
Receiver
Linux Computer
Data
Data
In its simplest terms, the remote accepts user and battery input, and will output infrared
data to the receiver. The receiver both draws power from and communicates with the PC.
Level 0 Functional Requirement Tables
Designers: Brent Gamble, James Williamson
Table 3: Remote Control Unit FR Table
Module
Input
Remote
Power
User input
Modulated serial data
Provides user interface to software commands
through buttons and sends the data over
infrared channel.
Output
Functionality
13
Table 4: USB Receiver FR Table
Module
Input
USB Receiver
Serial data from remote
Data from Linux driver
Power from the PC USB port
Data to Linux computer driver
Reads serial data transmitted from the remote
control and sends the commands to the PC
through USB.
Output
Functionality
Level 1 Block Diagram
Designers: Brent Gamble, James Williamson
The next layer of abstraction sees the design broken down into smaller building blocks
that represent a mid-level view of the devices, focusing on how the components
interconnect. Level 1 represents the natural progression down to smaller black-box
circuitry.
Figure 8: Level 1 Hardware Block Diagram
User Input
Remote
Receiver
Buttons
Data
PIC24
PIC24
Data
Linux Computer
Data
Power
Source
Transmitter
Power
IR receiver
Level 1 Functional Requirement Tables
Designers: Brent Gamble, James Williamson
Table 5: Remote Microprocessor (PIC24FJ64GB002) FR Table
Module
Input
Output
Functionality
Remote Microprocessor (PIC24FJ64GB002)
Activated pushbutton
Power
1 bit [PinZ => LED]
Programmed to poll => encode => modulate.
14
Table 6: Remote Control Transmitter FR Table
Module
Input
Output
Functionality
Transmitter
1 bit (PinZ => LED)
Power (3V)
Infrared binary signals (LED => TSOP_IN)
Over the air infrared communication of at least
30 feet.
Table 7: Remote Control Power Source FR Table
Module
Input
Output
Functionality
Remote Controller Power Source
N/A - - this is the circuit power source
Approximately 2700mAH at 3 volts
Powers the circuit.
Table 8: Remote Control Buttons FR Table
Module
Input
Output
Functionality
Button Circuit
User-activated mechanical pushbuttons
Signal to PIC for data collection
User interface for input capture.
Table 9: Receiver Microprocessor (PIC24FJ64GB002) FR Table
Module
Input
Output
Functionality
USB Receiver Microprocessor
(PIC24FJ64GB002)
1 bit [TSOP_output => PinX]
Power [USB=> Vdd]
Driver data
USB standard data [PIN+ => D+ ]
Decode => Interface USB.
Table 10: Receiver Infrared ICR Component FR Table
Module
Input
Output
Functionality
IR receiver ( TSOP 1738 )
Binary infrared signals [LED => TSOP_input]
Power: upto +6V [USB => Vs ]
1 bit [ OUT => PinX]
Constantly accepts incoming LED signals.
15
Level 2 Block Diagram
Designer: James Williamson
The final stage of abstraction before component-level schematics is depicted in Figure 9.
The transmitter and receiver components are further broken down to their building blocks
and the pushbutton array is depicted as being polled row-by-row and outputting a value to
the column bus going into the PIC24. Note for convenience of reference, the circuits are
color coded:


Green blocks represent the remote control transmitter circuit schematic from figure 4
The red block represents the remote control pushbutton circuit schematic from figure 2
Figure 9: Level 2 Hardware Block Diagram
Remote
User Input
Receiver
Data
Pushbutton Array
PIC24
Rows
Columns
Data
Transmitter
PIC24
Regulator
BJT
AA
Batteries
Modulated
Data
LED
Linux PC
Power
IR Receiver &
Filtering Capacitors
Due to its simplicity, the entirety of the orange block is represented by the receiver
circuit schematic in Figure 6.
Level 2 Functional Requirement Tables
Designers: Brent Gamble, James Williamson
Table 11: Remote Pushbutton Array FR Table
Module
Input
Pushbutton Array
Row select (3V polling)
User-activated pushbutton
[Row, column] of corresponding button
Uses a row-column array to determine which
button was pressed on the remote. The row and
column selects will be polled to check which
combination was active.
Output
Functionality
16
Table 12: Remote NPN BJT FR Table
Module
Input
Output
Functionality
BJT (NPN)
Weak 1 bit encoded signal
3V rail at collector
Amplified 1 bit encoded signal
Used to drive current through the LED for a
desired 30-foot infrared transmission distance.
Table 13: Remote IR LED FR Table
Module
Input
Output
Functionality
IR LED
Amplified 1 bit encoded signal current
Pulse of infrared light corresponding to logic
value
Wirelessly transmits the signal via light to the
receiver, eliminating the need for wires.
Table 14: Remote 1.5V AA Batteries FR Table
Module
Input
Output
Functionality
Two 1.5V AA batteries in series
N/A - - this is the circuit power source
Approximately 2700mAH at 3 volts
Responsible for powering the PIC24 and
transmitter in the remote.
17
PCB Design and Application
Designer: James Williamson
To implement the remote hardware while adhering to the packaging constraints, it was
necessary to design a two-layer printed circuit board. On the first (top) layer, interlocking
copper pads were arranged in a 4 by 10 grid to satisfy the button requirements. The rows
and columns were attached by copper-plated vias to the second (bottom) layer, which
housed all electrical components, the microcontroller, and the battery contacts. Figures 10
and 11 below depict the rendering of the finished layout in Sunstone's PCB123 software:
Figure 10: PCB Layer 1 Rendering (Top)
Note that because of the low clearance that would be present between the remote casing
and the board top, all electrical components are placed on the bottom layer.
18
Figure 11: PCB Layer 2 Rendering (Bottom)
As shown above, the vias connect at each column on the bottom, while the rows are
primarily routed over the top. This simplified routing and made analyzing traces easier to
the engineer.
Figure 12: PCB Top Photograph
Photo taken by: James Williamson
19
Figure 13: PCB Bottom Photograph
Photo taken by: James Williamson
Figure 13 illustrates the components soldered onto the circuit board bottom. Present in
the photo are the biasing resistors, BJT, IR LED, decoupling capacitors, battery contacts,
battery casing, microcontroller, and diagnostic power switch (to be removed post-testing).
Figure 14: Remote Internal Photograph
Photo taken by: James Williamson
20
Because finding appropriate pushbuttons for the project was difficult (manufacturers are
both reluctant to sell in any non-mass-produced quantity, and are often found overseas),
cheap and simple universal remotes were scavenged for their normally-open silicone
pushbuttons. In Figure 14, the remote casing top is depicted. Buttons were cut from other
remotes and placed uniformly in the housing. Cork layers were added to the top and
bottom for extra protection against shorts that could possibly occur with the aluminum
below the painted surface and the PCB leads. On the pushbuttons above, the graphite
pads that will complete a row-column circuit are easily distinguishable.
Figure 15: Assembled Remote Photograph
Photo taken by: James Williamson
The image above represents the finished prototype. (Note: it was a completely amazing
coincidence that the buttons taken from other remotes almost perfectly matched the
MythTV logo theme colors.)
21
Receiver Design and Application
Designer: James Williamson
Figure 16: Assembled Receiver Photograph
Photo taken by: James Williamson
Visible in the photograph above is the prototype receiver. Below the USB jack the
voltage regulator is visible; red lines indicate 5V rails where orange lines indicate 3V
rails. This alleviated confusion with assembling and testing the circuit as the TSOP IR
receiver required 5V and the microcontroller required 3V. The three capacitors behind the
TSOP were used to filter the rails to the component (the blue line is the IR data line to the
PIC). Once the processing in the microcontroller is completed, data is sent to and from
the PC using the green and white USB data lines.
As the circuit required frequent testing (to ensure the TSOP was receiving the correct
message/message over a long range), two large leads were added for troubleshooting
purposes. In this manner, an oscilloscope could obtain quick reads from the input and
ground lines. More details are discussed on this in the testing sections.
22
Design Considerations and Calculations
Author: James Williamson
This section serves as a natural progression from the theory of operation, encompassing
details about the components, modes of operation, and circuit parameters. When
designing the hardware for a project such as this, it is important to analyze the circuit to
determine what to expect before the pieces are connected and any components are
operational. While the low-power nature of this project minimizes some areas of concern,
care must still be taken to protect, for instance, the diode in the transmitter, which is
receiving an amplified current (for the sake of transmission, we wish to maximize the
current to achieve the full distance specification, but must take caution not to exceed the
peak steady current rating).
Remote power:



Remote power is internal, consisting of two AA batteries wired in series so as to
achieve an output voltage of 3V (henceforth referred to as the „rail‟).
No regulator or voltage transformation is required, as the PIC24FJ64GB002
operates in the range of 2 – 3.6V.
Rating for an alkaline AA is typically around 2700 mAH.
Arguably the number one point of interest for the remote control is battery life. In
order to estimate battery life, we must consider the current drain. The PIC24, per its
datasheet specification, can be expected to draw typically 11.3mA operating under 3V
at 16 MIPS (million instructions per second) when operating, and 100 uA when idle.
Similarly, the transmitter will drive the LED at close to 400mA when transmitting.
Thus, when the remote is operating, assume the maximum current sunk by the circuit
is 411.3mA, and when it is idle (awaiting input), it sinks 0.001mA.
Now, the variable factors. For the purposes of obtaining a rough value, let us assume
that for every hour of TV watched, the remote is actively managed 30 seconds of that
time. For those 30 seconds, the user is pressing buttons for 15 seconds. For those 15
seconds, maybe there is electrical contact for a tenth of that time, and for that time,
perhaps the circuit is fully active for a tenth of it. This means for every hour (3600
seconds), the circuit could potentially be active for 15 × .1 × .1 = .15 seconds. This
results in 0.004167% of that hour. Assuming an average user watches TV one-twelfth
of the time (14 hours a week), 0.004167 * (1/12) = 3.4725 * 10-4%. Let us refer to
this percentage as TimeActive. Contrarily, for time (1-TimeActive), suppose the PIC
is idle, awaiting command, and the transmitter is off, so the circuit is running at
100uA.
23
With these conditions of use, we could expect a rough battery lifetime of:
This results in 18904.39674 hours or 787.7 days – roughly 2 years. Changing the
batteries in a remote once every two years is a plausible estimate for most
contemporary models, if a bit optimistic. Compounding this ideal is the fact that
batteries‟ efficiencies can suffer at variable discharge rates (the energy of the cell is
discharged less efficiently at higher rates). Chalking up an additional 25% to battery
inefficiencies, aging, or losses would result in a year and a half of operation, a decent
benchmark.
It is also useful to consider worst-case power consumption. The PIC24 can draw a
maximum of 250mA into the source pin. While this design will almost certainly never
approach that value, it is theoretically possible. Thus, the absolute worst-case draw is
650mA at 3V, resulting in 1.95 watts power consumption for the remote.
Remote pushbutton array:



There are 39 total remote commands requiring a 4-row, 10-column circuit to meet
all possibilities.
At any given time, only one row is actively polled at 3V.
The columns that “light up” while the row is polled signify buttons being pressed.
The premise and functionality of the hardware side of the keypad are quite
straightforward. Rows 0 – 9 are connected to PIC output pins. Periodically, a voltage
is applied to each row, but no current flows because of the normally-open pushbutton
state. If a button is pressed, the circuit is completed, and a current flows through the
resistor on one of the column paths (Columns 0 – 3) to ground. The nodes just above
the resistors, beyond the switches on each column, are connected to PIC input pins.
When these nodes register voltages higher than zero (open circuit), the
microcontroller is now able to register which column(s) was activated for the current
row, obtaining a (row, column) value that can be encoded for a specific command.
As per the datasheet specification, the PIC I/O pins cannot source more current than
they are able to sink, so this should not be an issue, even for the theoretical
maximums of 25mA. For a circuit schematic, see Figure 14 in the simulation section.
24
Remote transmitter:



The transmitter achieves signal amplification utilizing an NPN BJT, with the base
connected to the PIC output, collector at 3V rail, and emitter connected to the
LED path (see Figure 4).
The LED has a peak steady current rating of 400mA and a theoretical maximum
range specification of 35 meters. Our design requirements specify a 30-foot range
of operation (26% of that distance).
Communication is achieved by pulsing light across the infrared spectrum, which
is invisible to the human eye and does not permeate opaque objects, limiting
interference.
The modulated message signal coming from the PIC24 for a button press is quite
weak, and for transmission across the noisy real-world distance it must cover, it needs
to be amplified. The bipolar junction transistor is a component suited to this task, as
operation in the forward-active region for the component allows for a much larger
collector-emitter current that is proportional to the base current. For an NPN to “turn
on,” the base voltage should be pulled high relative to the emitter. Additionally, to
operate in the forward-active region (which is the ideal region for this application),
the collector voltage must be higher than the base. Summarily, Vc > Vb > Ve.
For the approximate bias point at which we wish to run the transmitter, a resistor
value at the PIC24 output (logic high 3V) must be chosen to provide an adequate
voltage drop so that the voltage present at the base is below the 3-volt rail. Resistor
values of 10 ohms and 1 ohms were chosen for the base and emitter nodes,
respectively; the idea was to produce a transmitter design with a base voltage of
approximately 2.5 - 2.7 volts (to ensure it operates in forward-active mode) and an
emitter voltage of around 1.8 volts or below – to make certain the BJT cut-in voltage
(typically around 6/10 of a volt for nominal silicon BJTs) is surpassed in order to
“fully” turn on the component. The values were tweaked to drive a current through
the LED close to (but not quite) 400mA, the peak steady current. Thus, for active
operation, the DC current gain can be described as:
So, theoretically, we expect to see a DC current gain of ~133 if the PIC I/O outputs
3mA base current.
25
Remote microcontroller:
The PIC24FJ64GB002 was chosen for this application because of a combination of
design requirements:





15 I/O pins are required – 10 outputs for pushbutton rows, 4 inputs for column
rows, and 1 output for the modulated signal.
The voltage operating range is 2 – 3.6 volts, which is compatible with the 3-volt
power source.
It is a PIC24, and thus compatible with the code developed for Brent and TJ‟s
Embedded Systems Interfacing project.
The microcontroller possesses 5 input capture modules, 5 16-bit timers, and USB
support built-in.
The PIC is available in a 28-pin dual-inline package.
Receiver:
The USB receiver simply consists of an IR receiver tied to a PIC24 input pin. Upon
receiving a signal, a 5mA (maximum rating) current is fed to the microcontroller
input (no resistor is necessary as the pins are able to sink up to 25mA). The PIC will
decode the message and prepare the data for transfer over USB. However, because
the TSOP receiver requires 5 volts and the PIC requires 3, a regulator will be
necessary on the VCC pin that can accommodate a 5-to-3 volt DC-DC conversion.
USB 2.0 is capable of delivering 4.4 – 5.3 volts over 5 unit loads maximum. A unit
load is defined as 100mA. If a device drawing power requires more than half an amp,
it must provide an external source. This design can pull an absolute maximum of 255
mA per the TSOP and PIC24 maximum ratings, making external power unnecessary.
At peak operation, power consumed in the circuit is 5V*255mA = 1.275 watts.
26
Simulations
James Williamson
Figure 17: Simulation of Remote Transmitter
Assuming a PIC I/O pin output voltage of 3 volts (which will probably vary slightly) we
can expect a base current of roughly 4mA and a collector current of about 385mA.
Table 15: Remote Transmitter Simulation Legend
Power Rail
PIC I/O
NPN Collector
NPN Base
NPN Emitter
Voltage (V)
3
3
3
2.601
1.739
27
Current (mA)
381.32
3.994
381.32
3.994
385.31
Thus, common-emitter and common-base current gains are:
It is important to consider the effects of voltage ripple from both the rail and PIC output
on operation of the LED, to prevent damage to the component from excessive current.

Figure 11 shows a DC sweep in voltage across the PIC output from 2.7 to 3 volts.
The current through the LED varies from 312 – 385mA.

Figure 12 depicts a change in the source rail from 2.5 – 3.5 volts, which will
realistically vary during the life of the battery. The current through the LED varies
less than +/- 2mA, making it rather insignificant in terms of impact on the
transmitter. If the collector voltage slips below the base current, the BJT will enter
saturation and will still function as a switch in “on,” having a minimal impact, in
this case, on the LED current.

Figure 13 depicts an example message sent from the PIC, with PIC output
voltage, base current and emitter current waveforms.
Changes over the course of design:
The only significant hardware change was the move from utilizing a 2N2222 NPN
BJT to a 2N4401 NPN BJT. During the course of testing, one of the biasing testing
circuits utilized the 4401 because it was observed to have a slightly higher gain
(about 30mA), and upon comparison of the datasheets, it was found that the 4401
offered slightly better amplification at the cost of a higher maximum collector
current at 600mA. Because this is still 200mA higher than our IR LED maximum,
this did not impact the design negatively in any meaningful way. Thus, the design
moved forward with the 4401. However, there was no accurate model for this
transistor in SPICE, and so the prior 2222 simulations are retained for simplicity‟s
sake; they are conceptually equal and very close qualitatively. The biasing transistors
were adjusted accordingly to reach similar regions depicted below. At the battery‟s
maximum voltage of ~3.2V, the current through the LED should come close to but
not exceed 400mA. In the case of supply wiggle, the maximum voltage should never
cause this value to damage the LED.
28
Figure 18: Waveform Depicting PIC Output Pin Voltage Ripple Effect on LED Current
29
Figure 19: Waveform Depicting Source Voltage Ripple on LED Current
30
Figure 20: Example PIC24 Output Voltage Pulse Train with Base and Emitter Current Waveforms
31
Figure 21: Schematic of a Simplified Single-Column/Row Remote Pushbutton Circuit
Figure 14 illustrates a simplified path that represents one row-column combination
(button). When the row is polled high, and a switch is pressed, a voltage will be present at
the node „V‟ marked above. Figure 15 contains a waveform depicting the change in
current and voltage upon closing the pushbutton. 600uA runs from the pins at 3 volts
(0.0018 watts dissipation across the resistor).
32
Figure 22: Pushbutton Circuit Voltage and Current Waveform
33
3b. Software Design
Software Theory of Operation
Remote
Author: Brent Gamble
The remote control has two main subsystems: one for detecting which button is pressed
on the remote, and a second for transmitting data about that button press to the receiver.
The first subsystem, button detection, works under the following theory: each button on
the remote is effectively a switch that, when closed, completes a path between its
respective row and column as seen in Figure 2. This grid architecture allows the rows to
be scanned. The columns will detect a change when a switch on that line is closed and
being polled, allowing the software to determine the button by mapping the rows and
columns.
Each column (C0 through C3) in the grid will be connected to its own pin on port A of
the PIC. Similarly, each row (R0 through R9) will have its own designated pin on port B
of the PIC. On initialization of the remote, input capture modules will be assigned to each
column. These will be triggered on a rising edge. A 150ms timer turns on and drives each
row HIGH for a period of 15ms in succession. Because of the grid style layout in
hardware, if a button is pressed the corresponding column will have a change from logic
LOW to logic HIGH.
When a specific column detects the logic change, its interrupt routine is called. The first
thing is to mask all input capture interrupts, because only one button press can be
processed at a time. The routine then stores a copy of the current row global variable
before it changes. The row polling is still going on at this time, just no button presses are
listened for. The interrupt then starts the transmission subsystem by invoking
generateMessage() and passing the current row as well as the identifier for the column.
The transmission subsystem is only enabled when invoked from a button press. This
extends battery life, since transmission through the LED consumes the most power. The
transmission system starts at the generateMessage() routine. The routine is passed a row
and column identifier. These are used to pull a 6 digit code from the look-up table, unique
to each row/column combination. See appendix B for the codes assigned to each button.
Before sending this message, it must be prefixed with a 3 digit header, per RC-5
specification. The first two digits of the header are a „1‟, and the third is a toggle bit. It
was discovered the toggle bit is not needed in the design, and so this third bit is also a „1‟.
With a complete message made, the message is stored in a buffer and the 2 transmission
timers are enabled. The first timer is a fast timer that operates at 2 x 38kHz, or 13.1us.
There is a pointer to the current digit in the message buffer. For the first 64 timer
interrupts, if the digit at the pointer location is a „0‟ then the output toggles between high
34
and low. This produces 32, 38kHz pulses on the output pin. After the first 64 interrupts,
the output is left at logic LOW. The opposite is done if the message is a „1‟. This
produces Manchester coded data as per Figure 24.
The second timer is slower. It operates at 1.6ms, and every interrupt increases the pointer
to the digits in the message. When the end of the buffer is reached, both timers are turned
off since the message has now been sent on the output pin. The input captures are enabled
again, and the whole cycle is repeated.
As previously mentioned, the implementation (Figure 23) is derived from the Phillips
RC-5 protocol, which is widely used throughout the remote control industry. Being
derived, the implementation is not the RC-5 compliant, and differs from the protocol in
three key ways. The first is that the duty cycle of the 38kHz pulses are 50%. RC-5 says
the duty cycle should be between 25% and 33%. Duty cycles this low help save power on
transmission. Power is much less of a concern with all with all work being done in
software, and with a very power friendly PIC. 50% duty cycle was chosen as an
acceptable solution, because of the ease of implementing 50% in software since power
consumption is already low.
The second difference is that RC-5 calls for a 5-bit address after the toggle bit. This
allows codes to be differentiated between devices, and is how universal remotes
distinguish between products. There was no need to include the 5-bit address code in this
first design, and so it was omitted.
The final difference is 150ms of delay between repeated messages, and RC-5 calls for
only 114ms. This is due to the fact that there is 150ms of delay between subsequent polls
of the same button. These small changes helped ease development and the increased
timing on both the duty cycle and between messages helps on the receiver. It must be
noted that timing cannot differ too far from RC-5, as the receiver hardware is designed to
handle the timing requirements of this protocol.
35
Figure 23: Project Implementation of the RC-5 Protocol
1 cycle
38 kHz - 26.3uS
32 cycles
1.6ms
1
1
Start
1.6ms
1
1
0
0
Toggle
1
1
Data
15.1ms
150ms between repeated signals
Figure 24: Manchester Coding as Per IEEE 802.3
“1”
“0”
36
0
Receiver
Author: Brent Gamble
The receiver has two main subsystems: one for receiving data from the infrared receiver,
and a second for sending the message to the computer through the USB interface.
RC-5 timing is used for transmission because of physical limitations of the TSOP infrared
receiver component. The TSOP has the following limitations, as copied from the
datasheet[11].
1. Burst length should be 10 cycles/burst or longer.
2. After each burst which is between 10 cycles and 70 cycles a gap time of at
least 14 cycles is necessary.
3. Up to 1400 short bursts per second can be received continuously.
It is because of these limitations that Manchester coding is used. Without Manchester
coding, only three „1‟s could be sent in a row using RC-5 timing. Manchester coding
prevents a constant burst of logic HIGH on sequences of „1‟, so even if the same bit is
sent repeatedly the timing restrictions of the TSOP receiver are not violated.
The TSOP is active low. This makes IEEE 802.3 Manchester encoding appear as original
Manchester coding to the receiver. The receiver PIC is setup with an input capture on pin
4. The input capture is waiting for a falling edge, indicating the first „1‟ in the header as
defined in the protocol specification. A 1.6ms timer is initialized, and the input capture is
disabled while reading the message. Every time the timer interrupts, it grabs the data on
the line, effectively sampling the data at every bit. This sampling determines if the bit is a
„1‟ or a „0‟ by sampling only the second half of every bit in the message. When all 9 bits
are read, it is stored in a buffer and checked for correctness. The message is assumed to
be correct if the first three bits are a „1‟. The timers are disabled, and the input capture is
enabled for listening to the next message.
After the message is checked and found correct, it is sent off to USB. The PIC is
programmed with Microchip‟s USB stack to facilitate USB communication. USB is done
in polling mode, meaning requests from the attached computer are buffered until the PIC
gets around to handling them. The USB handling is done in the ProcessIO() function,
which is on a continuous loop with other functions. When a message is ready for USB
transmission, the message is copied into a buffer accessible in the ProcessIO() function.
When the Linux Computer sends a command 0x81, the contents of this buffer are
returned to the computer and the buffer is then cleared. If no messages have come in
since the last poll, the buffer contents are returned to the Linux computer empty.
37
Linux USB Driver
Authors: Brent Gamble, Thomas Cramer
The receiver for the remote control plugs into the Linux computer via a USB port. Like
all peripherals interacting with a computer, the receiver requires a driver to transform
common high-level device calls into low-level hardware interactions specific to the
device. Typical devices like a mouse, keyboard, or USB thumb drive are programmed to
perform as a common USB device class, such as HID (Human Interface Device). Most
operating systems provide generic drivers for these common classes and so no additional
third party drivers are necessary.
The receiver does not implement one of these common classes, and so an additional
driver is needed. Because drivers typically interact right on the hardware, they are
required to run with special access on the operating system called “kernel space.” The
original design called for writing a driver that would load with the operating system and
run in this “kernel space.” Doing so requires a deeper knowledge of the operating system
than writing a typical user application. However, due to a late start from PIC
programming problems, it was decided to opt for something that could be completed in
the remaining timeframe.
Applications that do not run in “kernel space” are said to run in “user space.” They are
limited in access to the hardware and require additional overhead when accessing the
peripherals attached to the computer system. However, by being more comfortable with
typical applications running in “user space,” the turnaround for the driver was quicker
which would leave more time for integration. It was decided to use a “user space” library,
called LibUSB to aid in interfacing with the developed receiver.
The LibUSB library is an open source library included with most Linux distributions that
provides clean interfaces for working with any USB device. However, by not being a true
driver in “kernel space”, the application is missing integration with the operating system.
It must start after the operating system, and is not informed when USB devices connect.
Thus, additional configuration is required to start the application after the operating
system starts.
After the Linux USB driver is started either manually, or automatically with the system; it
must first try and find the MythTV Driver (which is actually a library). It should be noted
that any library could be loaded which has the receiveData() function available. If the
MythTV Driver is found, the next step is to initialize the LibUSB library and connect to
the device. Connection is done by searching all connected USB devices and finding one
with a DeviceID of 0x048D. When a connection is made, the Linux USB driver can now
interact with the receiver.
The Linux USB driver periodically polls the receiver for data. The driver sends a
command 0x81 to the receiver, which upon reception returns the contents of a buffer.
This buffer is the latest command from the remote, or 0 if nothing new has been received.
38
The data returned from the receiver is a character array embedded in a libusb_transfer
structure. Each character in the array represents one digit in the message. An algorithm
constructs the original message to an int from the array, and the contents are checked.
All valid messages start with 111. Therefore, if the message is greater than 111000000 (in
binary), then it is assumed correct and forwarded to the MythTV driver. Recall the
MythTV driver was loaded when the Linux USB driver was first launched. The MythTV
driver exposes a function called receiveData(). The Linux USB driver calls the MythTV
driver by passing the message received from the PIC to this function, and then starts the
cycle again by sending a 0x81 to the receiver. To improve response time, the call to this
function is done a separate thread than everything previously. This allows the Linux USB
driver to continue to process I/O while the MythTV Driver is processing the last
command.
If the receiver is plugged in when the computer turns on, and the Linux USB Driver is
configured to start on system boot (default operation), then it starts running immediately.
All the above is transparent to the user and is a background process in the operating
system. The UML for the Linux USB driver, and all the remaining computer software is
documented on the following page in Figure 25.
39
Figure 25: UML Diagram of Linux Software Package
40
MythTV Driver
Author: Brent Gamble
To provide and abstraction between the device and the API of MythTV, the MythTV
Driver component was developed. This driver is not truly a driver; but rather some code
compiled as a library that the USB driver can utilize. This allows for the USB driver to be
decoupled from the application, so if the API changes the driver does not need rewritten.
Also, this would allow for the same USB driver code to control some other program just
by loading a different library.
The MythTV driver exposes a free function, receiveData() which accepts an integer
argument. The 9 bit code easily fits into an integer. The USB driver, upon receiving a
proper code from the receiver, will call the receiverData() function of the MythTV driver
and pass the integer.
There are three main ways to interface with the MythTV application. The first is by
utilizing the libraries MythTV provides, essentially the same way the USB driver
interfaces with the MythTV driver. This proved impractical at the end due to time. The
second way is to use Perl or Python bindings to control the program. The third way is to
communicate with MythTV through a network socket. The second option is being used,
specifically Python.
After the MythTV driver receives the integer from the USB driver, it creates an object
called MythTVDriver. This object initializes the python support from within a C++
program. The integer passed in is sorted to the appropriate call, and dispatched to the
Python bindings. The Python bindings are implemented in a separate Python file.
Turning on and off the MythTV application from the remote required a third small piece
of code in the MythTV driver. Starting the MythTV application from the driver meant the
application would close when MythTV driver thread was stopped. A shell script was
created that when executed would start MythTV if it was off, or turn it off if it was on.
The MythTV driver calls this shell script when the matching code for this button is
received.
All the software on the Linux PC is running in the background with no GUI. Having the
code crash would result in the user having to manually check if it was running and the
restarting it. For this reason, the software was developed with as many checks as
possible. Pressing a button on the remote with the application not running should not
break anything! All code was run through a free tool called Valgrind. This tool profiles
the code and executes the application looking for any problems and memory leaks. The
final product is free of any such errors.
41
Installation
Author: Brent Gamble
One key feature of the entire remote control USB solution is ease of install with the
Linux operating system, keeping the average user away from complex configuration files
and software internals. The USB driver and MythTV driver will be installed using a shell
script, the Linux equivalent of a Windows batch file. All the user has to do is run the shell
script, and after answering a few questions the entire solution will be installed without
any knowledge of the underlying file system. The shell script first checks for
dependencies, such as Python and MythTV. It then copies the files to the proper location.
It asks if the driver should run when the system starts, and then asks if the driver should
be started now.
Level 0 Block Diagram
Designer: Thomas Cramer
Figure 26: Level 0 Software Block Diagram
User Input
Poll For Buttons()
MythTV RC USB Driver Package
Data
Receive USB()
Send USB()
Send Data()
Modulated
Data
Receive Data()
Application
MythTV
Command
Remote
USB Receiver
42
MythTV
Frontend
Linux Computer
Level 0 Functional Requirement Tables
Designers: Brent Gamble, Thomas Cramer
Table 16: Poll For Buttons() FR Table
Module name
Module type
Input arguments
Output arguments
Description
Modules invoked
How invoked
Poll For Buttons()
Output
None
row: Number of row which the button is on
column: Number of column which the
button is on
Polls the rows and the columns to find a
depressed button. Calls to Send Data() with
the row and column identifier.
Send Data()
Timer polls each row for 10ms, 150ms
total for all rows.
Table 17: Send Data() FR Table
Module name
Module type
Input arguments
Output arguments
Description
Modules invoked
How invoked
Send Data()
Input
row: Number of row which the button is on
column: Number of column which the
button is on
None
Takes the row and column numbers to form
a bit pattern unique to that input
combination and send that data out through
the transmission hardware.
None
Called
43
Table 18: Receive Data() FR Table
Module name
Module type
Input arguments
Output arguments
Description
Modules invoked
How invoked
Receive Data()
Output
None
data[]: The buffer of data that should be
sent through the USB line
Takes the data coming in from the receiver
hardware and extracts the necessary info to
send to USB. The info is 9 bits: a 3 bit
header and 6 bit message. All 9 bits are sent
for transmission over USB.
Send USB()
Triggered by input capture on a falling
edge
Table 19: Receive USB() FR Table
Module name
Module type
Input arguments
Output arguments
Description
Modules invoked
How invoked
Receive USB()
Output
None
data[]: The buffer of data that should be
sent through the USB line
Listens for any commands from the USB
line, such as DeviceID, status, etc. The data
stream is stored and processed. Then
invokes Send USB() with the proper
response.
Send USB()
Periodically polls the USB buffer for
commands
44
Table 20: Send USB() FR Table
Module name
Module type
Input arguments
Send USB()
Input
data[]: The buffer of data that should be
sent through the USB line
None
Takes the input message and wraps around
with any additional USB messaging that is
needed. Sends the new message out to the
USB transmission hardware.
None
Called
Output arguments
Description
Modules invoked
How invoked
Table 21: MythTV RC USB Driver Package() FR Table
Module name
Module type
Input arguments
Output arguments
Description
MythTV RC USB Driver Package
Input/Output
USB Data[]
MythTV Function Calls
Receives Data via USB and performs MythTV
interaction.
MythTV
Runs on system startup
Modules invoked
How invoked
Level 1 Block Diagram
Designers: Brent Gamble, Thomas Cramer
Figure 27: Level 1 Software Block Diagram
User Input
row
pollButtons()
Linux USB Driver
Port A
Input Capture
On Columns
Microchip
USB Stack
ProcessIO()
column
RB15
LUT
D-/D+
Port B
messageGenerate()
PIC24FJ64GB002
<libusb.h>
VENDOR_ID 0x04D8
uint remoteCode
connectToDevice()
send()
receive()
Asynchronous IO
Modulated
Data
Input Capture
USB Receiver
45
MythTV Driver
sendPlayStop()
MythTV RC USB Driver Package
PIC24FJ64GB002
Remote
uint remoteCode
Linux
Computer
MythTV
Frontend
Level 1 Functional Requirement Tables
Designers: Brent Gamble, Thomas Cramer
Table 22: pollButtons() FR Table
Module name
Module type
Input arguments
Output arguments
Description
Modules invoked
How invoked
pollButtons()
Output
None
row: Number of row which the button is on
Polls the rows by raising the PIN for that
row high. Informs the LUT which row is
currently being polled.
LUT
Timer polls each row for 15ms, 150ms total
for all rows.
Table 23: Input Capture on Columns FR Table
Module name
Module type
Input arguments
Output arguments
Description
Modules invoked
How invoked
Input Capture on Columns
Output
None
column: Number of column which the
button is on
Collectively a set if Input Change
interrupts, one for each row (4 out of 5
Input Change Hardware being used). When
there is a rising edge, send the column
number to the LUT and invoke
messageGenerate()
LUT, messageGenerate()
Triggered by Input Capture hardware
46
Table 24: LUT (Look-up table) FR Table
Module name
Module type
Input arguments
Output arguments
Description
Modules invoked
How invoked
LUT
Input/Output
row: Identifier for the row the button is on
column: Identifier for the column the
button is on
Code[]: the identifying code for that
row/column combination
Essentially a map of the button grid. Stores
the current row, and column so when
queried can give the information at that
location.
None
Called
Table 25: messageGenerate() FR Table
Module name
Module type
Input arguments
Output arguments
Description
Modules invoked
How invoked
messageGenerate()
Input
None
None
When an input capture module calls this
function, grabs the information for the
current row and column from the LUT. The
message is modulated and sent to the output
pin.
None
Called
Table 26: Receiver Input Capture FR Table
Module name
Module type
Input arguments
Output arguments
Description
Modules invoked
How invoked
Input Capture [Receiver]
Output
None
data[] – 9 bytes, 1 for each bit sent to the
USB Stack
Takes the data coming in from the receiver
hardware and extracts the necessary info to
send to USB. The info is 9 bits: a 3 bit
header and 6 bit message. All 9 bits are sent
for transmission over USB.
ProcessIO()
Triggered by input capture on a falling
edge
47
Table 27: ProcessIO() FR Table
Module name
Module type
Input arguments
Output arguments
Description
Modules invoked
How invoked
ProcessIO()
Input/Ouput
None
None
A consolidated single IO function for
interaction with the USB stack. Reads the
latest command on the incoming buffer. If a
request for the remote code (0x81), then
sends the data from the latest input capture.
None
Periodically called from main loop
Table 28: Linux USB Driver FR Table
Module name
Module type
Input arguments
Output arguments
Description
Modules invoked
How invoked
Linux USB Driver
Input/Output
USB Data[]
uint remoteCode
Periodically polls the receiver for data. When
new, valid data arrives sends the entire 9bit
message to the MythTV Driver.
MythTV Driver
Runs on system startup
Table 29: MythTV Driver FR Table
Module name
Module type
Input arguments
Output arguments
Description
Modules invoked
How invoked
MythTV Driver
Input/Output
uint remoteCode
MythTV Function Calls
Receives the entire 9bit message from the USB
driver. Decodes it to determine which button
was pressed. Makes the appropriate function
call in MythTV.
MythTV
Linux USB Driver
48
Level 2 Block Diagrams
Designers: Brent Gamble, Thomas Cramer
Figure 28: Level 2 Software Block Diagram
Modulated
Data
Port B
Poll Rows()
150ms
Timer
Yes
Input Capture
On Columns
ProcessIO()
Column
LUT
Timer 13.1us
6-bit
Code
Buffer
Linux USB Driver
Good
Message?
RB15
Row
Microchip
USB Stack
Buffer
Port A
D-/D+
User Input
messageGenerate()
<libusb.h>
VENDOR_ID 0x04D8
LIBUSB_ENDPOINT_IN
LIBUSB_ENDPOINT_OUT
uint remoteCode
connectToDevice()
send(0x81)
receive()
sendToMyth(remoteCode)
Asynchronous IO
Buffer
Timer 1.6ms
uint remoteCode
MythTV Driver
<Python.h>
receiveData( *remoteCode)
MythTVDriver::MythTVDriver()
MythTVDriver::sendKeyPlay()
“sendPlayStop”
MythTVDriver.py
sendPlayStop()
frontend.sendKey('stop')
Input Capture
MythTV RC USB Driver Package
PIC24FJ64GB002
PIC24FJ64GB002
Remote
Linux
Computer
USB Receiver
MythTV
Frontend
Level 2 Functional Requirement Tables
Designers: Brent Gamble, Thomas Cramer
Table 30: Poll Rows() FR Table
Module name
Module type
Input arguments
Output arguments
Description
Poll Rows()
Output
None
row: Number of row which the button is on
Polls the rows by raising the PIN for that
row high. Informs the LUT which row is
currently being polled.
LUT
Timer polls each row for 10ms, 150ms
total for all rows.
Modules invoked
How invoked
49
Table 31: Input Capture on Columns (L2) FR Table
Module name
Module type
Input arguments
Output arguments
Description
Modules invoked
How invoked
Input Capture on Columns
Output
None
column: Number of column which the
button is on
Collectively a set if Input Change
interrupts, one for each row (4 out of 5
Input Change Hardware being used). When
there is a rising edge, send the column
number to the LUT and invoke
messageGenerate()
LUT, messageGenerate()
Triggered by Input Capture hardware
Table 32: LUT (Look-up table)(L2) FR Table
Module name
Module type
Input arguments
Output arguments
Description
Modules invoked
How invoked
LUT
Input/Output
row: Identifier for the row the button is on
column: Identifier for the column the
button is on
Code[]: the identifying code for that
row/column combination
Essentially a map of the button grid. Stores
the current row, and column so when
queried can give the information at that
location.
None
Called
50
Table 33: messageGenerate() FR Table
Module name
Module type
Input arguments
Output arguments
Description
Modules invoked
How invoked
messageGenerate()
Input
None
None
When an input capture module calls this
function, grabs the information for the
current row and column from the LUT. This
information is prefixed with a 3 bit header
and stored in a buffer. The modulating timer
is turned on.
None
Called
Table 34: Timer (13.1us) FR Table
Module name
Module type
Input arguments
Output arguments
Description
Modules invoked
How invoked
Timer 13.1us
Timer
None
None
13.1us is the period of a half a 38 kHz
cycle. When enabled, this timer will bring
the output pin high and low at a 38kHz rate,
and the number of cycles high vs low is
dictated by IEEE 802.3 Manchester Coding.
None
messageGenerate()
Table 35: Receiver Input Capture (L2) FR Table
Module name
Module type
Input arguments
Output arguments
Description
Modules invoked
How invoked
Input Capture [Receiver]
Output
None
data[] – 9 bytes, 1 for each bit sent to the
USB Stack
The TSOP is active low, so when a falling
edge is detected then enable the sampling
timer and turn the module off.
ProcessIO()
Triggered by input capture on a falling
edge
51
Table 36: Receiver Timer (1.6ms) FR Table
Module name
Module type
Input arguments
Output arguments
Description
Modules invoked
How invoked
Timer 1.6ms [Receiver]
Timer
None
None
When enabled, keeps count of how many
cycles and periodically samples to capture
the first half of each bit. That data is put
into a buffer. When all bits are sampled,
turns off and enables the Input Capture
again. Invokes Good Message? to check if
the buffer can be forwarded to USB.
Good Message?
Input Capture
Table 37: Good Message FR Table
Module name
Module type
Input arguments
Output arguments
Description
Modules invoked
How invoked
Good Message?
Output
None
None
When invoked, checks that the first three
bits in the buffer are 1. If they are, copy the
buffer to the USB buffer.
None
Timer 1.6ms
Table 38: ProcessIO() (L2) FR Table
Module name
Module type
Input arguments
Output arguments
Description
Modules invoked
How invoked
ProcessIO()
Input/Ouput
None
None
A consolidated single IO function for
interaction with the USB stack. Reads the
latest command on the incoming buffer. If a
request for the remote code (0x81), then
sends the data from the latest input capture.
None
Periodically called from main loop
52
Table 39: Linux USB Driver FR Table
Module name
Module type
Input arguments
Output arguments
Description
Modules invoked
How invoked
Linux USB Driver
Input/Output
USB Data[]
uint remoteCode
Periodically polls the receiver for data. When
new, valid data arrives sends the entire 9bit
message to the MythTV Driver.
MythTV Driver
Runs on system startup
Table 40: connectToDevice() FR Table
Module name
Module type
Input arguments
Output arguments
Description
Modules invoked
How invoked
connectToDevice()
Function
None
None
Establishes connection with the USB receiver.
None
Called when Linux USB Driver starts
Table 41: send(0x81) FR Table
Module name
Module type
Input arguments
Output arguments
Description
Modules invoked
How invoked
send(0x81)
Output
None
0x81
Sends the command 0x81 to the USB receiver,
informing it to populate the USB buffer with
the latest message.
None
Called from Linux USB Driver infinite loop
Table 42: receive() FR Table
Module name
Module type
Input arguments
Output arguments
Description
Modules invoked
How invoked
receive()
Output
None
Data[]
Retrieves the contents of the USB buffer on the
receiver. This buffer should contain the latest
remote message. The data read in is stored as
uint remoteCode after some transformation.
None
Called from Linux USB Driver infinite loop
53
Table 43: sendToMyth() FR Table
Module name
Module type
Input arguments
Output arguments
Description
Modules invoked
How invoked
sendToMyth()
Input/Output
uint remoteCode
uint remoteCode
Passes the latest message from the receiver to
the MythTV Driver if the message is good.
MythTV Driver
Called from Linux USB Driver infinite loop
Table 44: MythTVDriver (L2) FR Table
Module name
Module type
Input arguments
Output arguments
Description
Modules invoked
How invoked
MythTV Driver
Input/Output
uint remoteCode
Python Function Calls
Receives the entire 9bit message from the USB
driver. Decodes it to determine which button
was pressed. Makes the appropriate function
call the the Python code.
MythTVDriver.py
sendToMyth()
Table 45: MythTVDriver.py FR Table
Module name
Module type
Input arguments
Output arguments
Description
Modules invoked
How invoked
MythTVDriver.py
Input/Output
None
MythTV Function Calls
Contains a function for every button
corresponding to an action inside MythTV.
When one of these functions is called, performs
the correct action inside MythTV.
MythTV
MythTV Driver
54
4. Operation, Maintenance, and Repair
Authors: James Williamson, Brent Gamble
Operation:
Remote:
The remote control unit functions as most other modern remotes do: it is composed
of 39 buttons mounted on a circuit board inside a protective casing. Inside the case
and under the PCB is a plastic battery housing carrying 2 AA batteries. There is a
power switch mounted internally for the sake of testing, but in production this would
be removed and the device would be always-on, awaiting input. In order to operate
the remote, the user need only press a button.
Software:
The software requirements for our MythRC software:
- Ubuntu based system
- LibUSB 1.0 (comes with some distributions, may need to install separately)
- Python 2.6
- MythTV (http://www.mythtv.org/wiki/User_Manual:Initial_Installation)
Installation is easy and consists of running our installation script as a root user. This
is a bash script that will place our files in the requisite locations:
- The Python bindings (MythRCDriver.py) are placed in /usr/lib/python2.6
- The MythTVDriver (libMythTVDriver.so.1) is the library that provides an
abstraction layer between the driver and the MythTVAPI. It is placed in
/usr/lib.
- The LibUSBDriver (mythremoted) is a user space application that
performs as a driver. It goes in /usr/bin.
- The helper script for starting/stopping MythTV (start_myth.sh) goes in
/usr/bin
- Daemon start/stop script is placed in etc/init.d.
Our script will register our daemon, which is started on OS boot. The daemon can
only run if the receiver is plugged in. If the receiver is unplugged, the daemon will
require manual startup; this is done by executing the following command with root
access:
service mythremoted start
When the daemon is active (it will begin on system startup), simply plug in the
receiver and start MythTV!
55
Maintenance & Repair:
If the remote requires a change of batteries, open the case and find the black battery
box under the printed circuit board. With a Philips screwdriver, remove the screws
and place 2 AA batteries inside.
It is possible during the course of operation that some buttons may not make clean
contact. Because the buttons are not manufactured specifically for this prototype, this
is a possibility. To check connection or to generally troubleshoot the silicone buttons,
remove the casing and the dowels that hold the PCB in place (the screws are
external) and lift the PCB out of the casing. The buttons are laid below the board and
are in segments. It is also a possibility that some of the individual button segments
could become dislodged if pressed too hard or yanked out of place, and you will
need to reseat the buttons in the same fashion.
When troubleshooting the remote, it is useful to observe whether or not the IR LED
is transmitting. Because infrared light is invisible to humans, you will need a cell
phone camera or similar device. While holding down a button, point your camera at
the LED and observe. If you see a pulsing purple light through the camera display,
the remote is transmitting properly. If not, check the soldering connections and
button contact and be sure you have no shorts/open circuits anywhere.
Inside the receiver casing are two leads (blue and black) that can be hooked up to
an oscilloscope to get a quick reading from the TSOP IR receiver. This is useful for
troubleshooting the receiving unit because it checks the integrity of the TSOP before
the microcontroller or other circuitry. Message checking or reception can be done in
this manner. If the problem is not obvious, a multimeter can also be used to verify
the 3V and 5V rails to ensure correct voltage is supplied to the PIC and TSOP as
well. The lines are color-coded for this purpose. Red is 5V and orange is 3V while
blue represents the data line to the PIC and black is ground.
56
5. Testing Procedures
Note:
The oscilloscopes at our design bench are not supported with such a new computer
system (new Dells with Windows 7 at the design stations). Such an oscilloscope, with
floppy drives and serial ports, does not have software support from Agilent to properly
capture screens. As a result, all oscilloscope printouts are simply the waveforms without
any axes and other useful information from the scope. Sorry for the inconvenience.
Remote Software
Author: Brent Gamble
The receiver is essentially broken into two subsystems: the polling subsystem to detect
button presses and the transmission subsystem. The first piece developed was the button
polling. The algorithm is simple, but relies on a 15ms delay on reading each pin. This
15ms x 10 rows gives 150ms delay between transmissions. If the 15ms is too short, then
the remote will saturate the receiver with messages and thus some will be lost. If the
15ms is too long, then response time of the remote will be unsatisfactory.
The first test is to make sure that the PIC is delaying on each pin for 15ms. This was done
by putting the oscilloscope to a specific pin and capturing the output. The oscilloscope
was then used to measure the time the PIN was held high. See Figure 29 for an example.
Figure 29: Pulse width of 15ms on PIN 6, or row 2 on the remote.
57
The next test is to make sure that the same row is being polled regularly and by about
150ms in between polls. This was done by using the same sample on the oscilloscope, but
grabbing more time to see multiple pulses. The oscilloscope was used to measure the
delay. It was found that from the rising edge of the first pulse, to the falling edge of the
second pulse was exactly 150ms. See Figure 30 for an example.
Figure 30: The same row at repeated polling cycles with delay of 150ms.
Clearly, the buttons are being polled accurately and within time constraints. The next test
is on the transmission subsystem. The results were not simulated at every algorithm;
instead the final result was measured on the oscilloscope to ensure validity. For instance,
ROW 2 was connected to COLUMN 3. The output, representing this button, sent to the
base of the transistor is seen in Figure 31.
Figure 31: Raw output for ROW 2, COLUMN 3.
58
The data is not very impressive until it is decoded. The algorithms output the signal in
IEEE 802.3 Manchester Coding (See Figure 24). The data must then be decoded by hand,
which is a relatively simple task. The LUT to encode the data is as follows:
Figure 32: Remote LUT for button codes
const unsigned char mLUT[ 10 ][ 4 ] =
{
{ 0x0, 0x1, 0x2, 0x3 },
{ 0x4, 0x5, 0x6, 0x7 },
{ 0x8, 0x9, 0xA, 0xB },
{ 0xC, 0xD, 0xE, 0xF },
{ 0x10, 0x11, 0x12, 0x13 },
{ 0x14, 0x15, 0x16, 0x17 },
{ 0x18, 0x19, 0x1A, 0x1B },
{ 0x1C, 0x1D, 0x1E, 0x1F },
{ 0x20, 0x21, 0x22, 0x23 },
{ 0x24, 0x25, 0x26, 0x27 }
};
When referring to ROW 2 and COLUMN 3, the reference is by zero indexing. So the
entry in the LUT for that combo is 0xB, or binary 00001011. Now analyze the data from
the oscilloscope with respect to the encoding and Figure 33 is the result.
Figure 33: Decoded output for ROW 2, COLUMN 3.
59
Per design, the upper 3 bits should be a 1 and the lower 6 should contain the message.
Clearly, the lower 6 are correct. This would indicate a “Channel Down” to be sent to
MythTV. This was done for every combination to ensure the message was correct.
Receiver Software
Author: Brent Gamble
The receiver is another piece that is broken into two key subsystems: the message
reception and decoding, and the USB transfer subsystem. The message reception
subsystem was tested again by the oscilloscope. One thing to keep in mind is that the
receiver is active low, so messages are coming in with the originally Manchester
encoding, and not IEEE 802.3. The first step on design, and testing, was to analyze the
receiver output compared with what the remote was actually transmitted. This was done
in Figure 34. The top graph is input to the receiver whereas the bottom is output from the
remote.
Figure 34: Receiver input versus remote output.
60
From Figure 34 it is seen that output is opposite the remote output. What is more
important is that small errors in the output from the remote as per the last part of the
waveform are not reflected in the receiver. Part of this is due to adhering to timing
requirements, and part due to selected capacitance values on the receiver.
The next thing to test is that the receiver is sampling the data at the correct time. This was
done by setting port RB1 high when sampling was done. Sampling was timed to always
capture the second half of every bit. Unfortunately the computer could not capture this
screen from the oscilloscope after many tries. However, it was found that the time of
sampling was correct and capturing the second half of every bit in the sequence.
The USB transfer subsystem is next for testing. However, there is no clean way to read
the data being transmitted across the USB lines. For this reason, testing was dependent on
the Linux USB driver being functional. The Linux Driver would print out the data it
would receive, and this was checked against what was being sent in the remote. More on
this is covered in the Linux USB testing section.
Linux USB Driver
Author: Brent Gamble
Testing of the Linux USB driver is pretty straightforward, and also covers testing of the
USB stack within the actual hardware of the receiver. The Linux USB driver, essentially
being a “user space” program runs as a console application. Unit testing was done during
construction of the driver, testing first the interaction through LibUSB with the device
and then the interaction with the MythTV driver. All tests were done by either analyzing
command line output or observing interactions within the MythTV application.
For interaction with the receiver hardware, the raw messages being sent to and from the
device are printed out to the console. Additional information relevant to the LibUSB
structures were printed to console as well for debugging. Figure 35 shows the command
0x81 being sent to the receiver, emphasized by the boxed text. Figure 36 shows the
response to a 0x81 command. In this particular case, no data was received since the last
request so the data is all 0. Underneath the box in Figure 36 shows the translated response
code from array of data. This data was used for testing and debugging communication
with the receiver hardware.
61
Figure 35: Sending a command 0x81 to the receiver to fetch the latest message from the remote.
62
Figure 36: Data returned from the receiver hardware, in this case no new command.
Testing communication with the MythTV driver is less complex. The MythTV driver
prints out a message to the console whenever its receiveData() method is invoked.
Sending commands and observing the output was the only viable way to test interaction.
Additionally, when everything was integrated the actual interaction with the MythTV
application was observed.
MythTV Driver
Author: Brent Gamble
The MythTV driver consists of three functional units. The first is a Python program
consisting of many free functions to control the MythTV application. The second is a
shell script which is used for launching and stopping the MythTV application when the
corresponding button on the remote is pressed. The third functional unit is a C++ library
that the Linux USB driver uses as the interface to MythTV. All the testing here has no
output that can be captured, but instead observed by watching the changes inside the
MythTV program, such as going into a menu, or changing the channel.
63
The Python program was written and then exercised with a small C program for testing.
This program would prompt for a 6 digit code, and based on the input exercise one of the
Python functions. The 6 digit codes correspond to the body of the message being sent by
the remote. A small subset of this C test program is included in Figure 37. The results
were to be observed in the MythTV program to verify the functionality.
Figure 37: Python Test Program
int main()
{
...
// C-Python Initialization stuff
...
while (1)
{
char remoteCode[6];
printf("Enter a binary six digit number: ");
scanf("%s", &remoteCode);
printf("You entered %s\n", remoteCode);
//Exit clause
if(strcmp(remoteCode, "000000") == 0)
return 0;
//Button 5 - send key page up
else if(strcmp(remoteCode, "000101") == 0)
{
pFunc = PyDict_GetItemString(pDict, "sendKeyPageUp");
....
//Other functions being exercised
....
The second functional unit is a shell script for starting and stopping the MythTV
application. The shell script was used to overcome numerous challenges with launching a
GUI-application from a system level service. The script is small with limited
functionality. It was tested by executing the script with system level access and observing
the application start and stop for the MythTV user.
The third and final functional unit is the core of the MythTV driver: a C++ library that
the Linux USB driver interfaces with directly. To test this code, a main method was
written into the library that would pass integers representing commands to the external
function. This essentially emulates what the Linux USB driver would do. See Figure 38
for this method. The library was compiled as an executable program for testing. The test
results were to observe the interaction with the MythTV application.
64
Figure 38: Method to test the functionality of the MythTV Driver
int main( int argc, const char* argv[] )
{
uint command = 0b111001010; //Move right
receiveData( &command );
...
//Other sequences of events
...
}
Installation Package
Author: Brent Gamble
The design requirement for ease of installation required an additional piece of code. This
is a shell script, similar to a Windows batch script. It copies the files to the appropriate
locations, and registers the daemon as to start on system boot and also starts the daemon.
To test this functionality, a clean system was used. After running it, the files appeared in
the proper location. Also, the list of system services included the driver and the status was
running. The latter two are seen in Figure 39 and Figure 40 respectively.
Figure 39: Linux USB Driver (mythremoted) is registered and starts when the computer turns on
65
Figure 40: Linux USB Driver (mythremoted) is currently running.
Remote Transmission Range
Author: James Williamson
In order to adjust the range at which the infrared LED can transmit messages clearly, the
amount of current through the LED must be controlled. The IR LED/TSOP receiver
combo datasheet (the two are sister components from the same manufacturer) specifies an
absolute maximum range of 35 meters for the peak steady current 400m[11].
Upon initial range testing (after PCB arrival and circuit construction), it was found that
the range was a disappointing 8-10 feet. Even at this reduced range, transmission errors
were common as well; these took on the form of unwanted “blips” where the bit would
flip on the oscilloscope readout. The current through the LED was suspect, so we decided
to measure how much was really supplied. The issue with this approach is measurement
itself. Because the signal coming from the PIC was modulated and constantly switching,
it was not as straightforward as using a multimeter to measure current on the emitter.
Thus, a simple biasing circuit was created (fundamentally that of Figure 4) with the two
differences being a) the PIC was programmed to constantly output a LOGIC_HIGH and
b) the collector was attached to the design station power supply instead of a battery. This
provided, via the supply readout, how much current was sunk into the BJT collector. With
the components from the simulation, the current was found to be 260mA, which was
undesirably low (and demonstrated that the simulation models are not always be perfect).
Upon experimenting with resistor biasing combinations and later, another transistor, the
IC value was increased by over 100mA to ~370mA. This greatly increased the range and
usability of the remote and satisfied the engineering requirement of 30 feet.
66
Figure 41: LED transmission as viewed through a cellphone camera in (left) lighted conditions and (right)
dark conditions.
Network Application
Designer: James Williamson
As part of a 4450:498 Communication Networking course project requirement, an
application was developed which interfaced with the MythTV frontend via a text-based
TCP socket. This found widespread use when testing because commands could be
quickly sent to the frontend control socket (commands through the networking interface
and Python bindings were syntactically very similar) and it could be observed what direct
function they were mapped to. A good portion of the project was developed in parallel, so
it became convenient to fine-tune the mappings before total system integration trial-anderror. As a result, some time was shaved off the debugging process.
Figure 42: MythGUI network application interface
67
This application accepted input as <keyword><argument> (ex. key x or play pause) and
replies socket echo for each sent command.
Remote Power
Author: James Williamson
The power for the remote was tested with the device turned on but not transmitting, to
gain an idea of how fast the battery would drain from the remote sitting unused. This was
prior to any explicit power saving software implementation on the microcontroller.
Table 46: Voltage drain on remote battery in non-transmitting state
Voltage
Time (mins)
3.176
3.171
3.167
3.161
3.159
3.158
0
57
105
191
233
258
Figure 43: Graphical depiction of Table 46 (y-axis voltage, x-axis time)
3.18
3.175
3.17
3.165
3.16
3.155
3.15
3.145
0
57
105
191
233
258
Obviously, a drain of 0.018V over about 4 hours means about 0.1V a day, which means
12 days of operation until the battery voltage hits that of the PIC threshold (2V). This is
68
clearly short of the mark required for such a device, and short of our project goals. By
putting the PIC into power saving mode via software instruction, the current required for
a non-transmitting state could go from 4-11mA (depending on instructions per second
and voltage) to about 100uA or less[12]. Unfortunately, because of time constraints, we ran
out of time and were unable to explore and implement power-saving options.
6. Financial Budget
Author: James Williamson
Original budget:
This is the originally submitted budget from SD1. Note that due to prior research, some
components were already purchased by team members and are not included in the overall
price. Because the PCB for the remote had to be custom designed and manufactured,
there was no set cost to work with until later into SD2.
Table 47: Original Budget
Qty.
1
2
1
1
1
1
1
4
5
5
1
39
1
1
2
Part Num.
TPS76630D
LR6XWA/B
ERD-S2TJ101V
ERD-S2TJ2R2V
ERD-S2TJ472V
FK24X5R1A475K
FK24Y5V1A106Z
PIC24FJ64GB002
Description
3V output linear voltage regulator
AA 1.5V battery
100 ohm quarter-watt resistor
2.2 ohm quarter-watt resistor
2N2222 NPN BJT - ALREADY PURCHASED
TSOP1738 IR Receiver - ALREADY PURCHASED
IR LED - ALREADY PURCHASED
4700 ohm quarter-watt resistor
4.7uF 10V general purpose decoupling capacitors
10uF 10V general purpose decoupling capacitors
Pushbutton PCB - must custom manufacture
Rubber Pushbuttons - must custom order
Custom Aluminum Remote Housing - DONATED
Custom Aluminum Receiver Housing - DONATED
PIC24 Microcontroller
69
Unit
Cost
$1.68
0.40
0.09
0.09
Total
Cost
$1.68
0.80
0.09
0.09
0.09
0.43
0.31
0.36
2.14
1.54
?
4.12
Total
8.24
$14.94
Budget Revisions:
Table 48: Revised (Final) Budget
Qty.
1
2
1
1
1
1
1
4
5
5
2
39
1
2
Part Num.
TPS76630D
LR6XWA/B
ERD-S2TJ101V
ERD-S2TJ2R2V
ERD-S2TJ472V
FK24X5R1A475K
FK24Y5V1A106Z
N/A
PIC24FJ64GB002
Description
3V output linear voltage regulator
AA 1.5V battery
100 ohm quarter-watt resistor
2.2 ohm quarter-watt resistor
2N2222 NPN BJT - PRE-PURCHASED
TSOP1738 IR Receiver - PRE-PURCHASED
IR LED - PRE-PURCHASED
4700 ohm quarter-watt resistor
4.7uF 10V general purpose decoupling capacitors
10uF 10V general purpose decoupling capacitors
Pushbutton PCB
Rubber Pushbuttons - taken from other remotes
Custom Aluminum Remote Housing - DONATED
PIC24 Microcontroller
Unit
Cost
$1.68
0.40
0.09
0.09
Total
Cost
$1.68
0.80
0.09
0.09
0.09
0.43
0.31
168.09
0.36
2.14
1.54
336.18
4.12
Total
8.24
$351.12
The obvious change to the budget is the pricing of the PCBs. Unfortunately, due to
Sunstone‟s ordering policy, 2 boards were the minimum quantity. The silicone
pushbuttons had to be extracted from obsolete universal remotes because no
manufacturer would oblige such a specific and small design. The receiver housing was
also changed; instead of an aluminum housing similar to that of the remote, a simple
plastic project box (provided by the ECE department free of charge) was used. Finally,
the 4401 transistor and a couple of resistors beyond the specification above were
provided by the ECE department via a parts request form later in the semester.
Due to a combination of tools provided by the department and open-source software, no
software purchase was necessary.
70
7. Project Schedule
Authors: James Williamson, Brent Gamble
Original schedule:
Table 49: Original Gantt Chart Table
71
Table 50: Original Gantt Chart Graph
72
Table 51: Revised Gantt Chart Table
73
Table 52: Revised Gantt Chart Graph
The major schedule changes were a result of the shift from a kernel-based USB driver to
a “user-space” LibUSB driver. Some time was spent on additional research but ultimately
sacrificed when the decision was made to forego the kernel-level development. The
hardware was more or less built on schedule, with little impedance to software
development. Small changes made in presentation over the course of the semester did not
affect functionality (or minimally affected function). Since the majority of this project
was developed in parallel with as few dependencies as possible, there was a conscientious
effort to minimize periods of extended time where progress was stalled waiting for
another subsystem to be developed.
74
8. Design Team Information
Author: James Williamson
The MythTV Remote Control System Project was proposed and developed in the Spring and Fall
2010 semesters by members of Senior Design Team 2, for the purposes of fulfilling the capstone
project requirement.
Brent Gamble, Computer Engineer
Project Manager
Brent is the project manager for the MythTV design team. Over
the course of Senior Design II, he designed and implemented all
of the embedded systems software for the remote and receiver.
Additionally, Brent contributed to Linux software development
design working on the Linux USB and MythTV Driver, writing,
profiling, and debugging code. He also prepared the installation
scripts for the software package.
Thomas “T.J.” Cramer, Computer Engineer
Software Manager
T.J. performed research for and contributed to early versions of
the kernel driver, and later, the Linux USB application required
for receiver-to-USB communication. He also provided code for
testing the MythRemoteDriver and Linux USB Driver.
James Williamson Jr., Computer Engineer
Hardware Manager & Archivist
During SDII, James served as the hardware manager, creating
and revising schematics for the remote and receiver in addition
to hardware testing. He also designed the printed circuit board
for the remote. On the software side, James wrote the Python
library that allows the MythTVDriver to control the application
frontend and contributed to software research and testing.
75
9. Conclusions and Recommendations
Author: James Williamson
This project represented a fantastic example of how the design approach builds a solid
foundation for a successful implementation, and allows the team to adapt to unforeseen
circumstance or system failure. Abstraction, a key concept taught at all levels of the
curriculum, becomes a focal point of the process. In order for team members to
successfully work together, subsystems and modules need to be designed to be welldefined so that they may be developed in parallel and integrated with the rest of the
design with ease. Developing the hardware, embedded system code, and Linux drivers in
parallel naturally presented a number of challenges when the time came for system
integration and testing. However, by relying on our education and thorough design
experience, the team was able to construct ways to ensure desired operation throughout
integration and testing. By adhering to an abstracted design and working in parallel, the
rest of the design is not at risk (or risk is minimized) when changes become necessary to
a subsystem. After having participated in both semesters, we strongly believe that this
senior design project succeeds in the program‟s objective of facilitating real-world
experience in the lab, and teaching students how to work not on just technical details, but
with each other.
Recommended for further reading:
About MythTV:
http://www.mythtv.org/detail/mythtv
MythTV Python bindings:
http://www.mythtv.org/wiki/Python_bindings#Frontend.28host.2C_port.29
76
10. References
Research Material and Textbooks:
[1] Lizy Kurian John, Charles Roth. “Digital Systems Design Using VHDL”. Page 209.
2007.
[2] Chris Coulston, Ralph Ford. “Design for Electrical and Computer Engineers: Theory,
Concepts, and Practice”. 2009.
[3] Bob Smith, John Hardin, Graham Phillips. “Linux Appliance Design: A Hands-On
Guide to Building Linux Appliances”. 2007.
[4] Jonathan Corbet, Alessandro Rubini. “Linux Device Drivers” O‟Reilly Publishing. Third
Edition.
[5] Michael Opdenacker. “Linux USB Drivers” www.free-electrons.com/doc/linux-usblabs.pdf
[6] Lucio Di Jasio. “Programming 16-Bit PIC Microcontrollers in C: Learning to Fly the PIC
24”
[7] Adel S. Sedra, Kenneth C. Smith. “Microelectronic Circuits” 2003.
[8] “Voltage Regulators” http://en.wikipedia.org/wiki/Voltage_regulator
[9] MythTV development community: http://www.mythtv.org/
[10] Libusb development resource: http://www.libusb.org/
[11] USB: http://en.wikipedia.org/wiki/Usb
Component Datasheets:
[11] Vishay TSOP1738 IR Receiver (sister component diode specs included):
http://www.datasheetcatalog.org/datasheets/208/301092_DS.pdf
[12] Microchip PIC24FJ64GB004 Microcontroller:
http://ww1.microchip.com/downloads/en/DeviceDoc/39940d.pdf
[13] Texas Instruments TPS76630D Voltage Regulator:
http://www.datasheetcatalog.org/datasheet2/f/0xe2ik3f0uauohcyps1ryuu33l7y.pdf
[14] 2N4401 NPN BJT Transistor:
http://www.datasheetcatalog.org/datasheet/fairchild/2N4401.pdf
77
11. Appendices
Appendix A: Remote Button Layout
Figure 44: Remote Button Layout
78
Appendix B: Remote Command Mappings
Table 53: Remote Command Mappings
Button
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
Function
Record
Application Power
System Power
Channel Info
Up Arrow
Mute
Channel Up
Left Arrow
Enter (Accept, OK)
Right Arrow
Channel Down
Chapter Back
Down Arrow
Chapter Forward
Volume Up
1
2
3
Volume Down
4
5
6
Play DVD
7
8
9
Live TV
Escape (Cancel)
0
Underscore
Rewind
Stop
Play
Pause
Python Mapping
frontend.sendKey('r')
N/A
N/A
frontend.sendKey('o')
frontend.sendKey('pageup')
frontend.sendKey('f9')
frontend.sendPlay('channel up')
frontend.sendKey('left')
Frontend.sendKey(‘enter’)
frontend.sendKey('right')
frontend.sendPlay('channel down')
frontend.sendKey('pagedown')
frontend.sendKey('pagedown')
frontend.sendKey('pageup')
frontend.sendKey('f11')
frontend.sendKey('1')
frontend.sendKey('2')
frontend.sendKey('3')
frontend.sendKey('f10')
frontend.sendKey('4')
frontend.sendKey('5')
frontend.sendKey('6')
frontend.sendJump('playdvd')
frontend.sendKey('7')
frontend.sendKey('8')
frontend.sendKey('9')
frontend.sendJump('livetv')
frontend.sendKey('escape')
frontend.sendKey('0')
frontend.sendKey('_')
frontend.sendKey('j')
frontend.sendPlay('stop')
frontend.sendKey('p')
frontend.sendPlay('speed pause')
79
Binary Code
0b00000001
0b00000010
0b00000011
0b00000100
0b00000101
0b00000110
0b00000111
0b00001000
0b00001001
0b00001010
0b00001011
0b00001100
0b00001101
0b00001110
0b00001111
0b00010000
0b00010001
0b00010010
0b00010011
0b00010100
0b00010101
0b00010110
0b00010111
0b00011000
0b00011001
0b00011010
0b00011011
0b00011100
0b00011101
0b00011110
0b00011111
0b00100000
0b00100001
0b00100010
35
36
37
38
39
Fast Forward
Switch Input
Home (Main Menu)
Scheduling Menu
Program Guide
frontend.sendKey('u')
N/A
frontend.sendJump('mainmenu')
frontend.sendJump('viewscheduled')
frontend.sendJump('programguide')
0b00100011
0b00100100
0b00100101
0b00100110
0b00100111
Appendix C: PCB Trace Width Calculation Table
Table 54: PCB Trace Width Calculation Table
TRACE: Transmitter Rail
Input Data
Field
Value
Current
400
Temp. Rise
10
Copper Thickness
Ambient Temp.
Conductor Length
Peak Voltage
1
25
6
3
Results
Units
mA
C
Trace Data
Required Width
Cross-Section Area
Value
0.23/0.0090551
0.01
Units
mm/in
mm2
oz/ft2
C
in
V
Resistance
Voltage Drop
Loss
Track Clearance
0.26
0.14
0.06
0.6
ohm
V
W
mm
TRACE: PIC Supply Rail
Input Data
Field
Value
Current
250
Temp. Rise
10
Copper Thickness
Ambient Temp.
Conductor Length
Peak Voltage
1
25
1.5
3
Results
Units
mA
C
Trace Data
Required Width
Cross-Section Area
Value
0.12/0.0047244
0
Units
mm/in
mm2
oz/ft2
C
in
V
Resistance
Voltage Drop
Loss
Track Clearance
0.17
0.04
0.01
0.6
ohm
V
W
mm
80
Appendix D: MythRCDriver.py Source Code
from MythTV import MythDB, MythError, MythLog
import sys, socket
# Author: James Williamson Jr.
#
The University of Akron, Spring 2011
#
Senior Design, Team 02: MythTV
#
Original Version: February 28, 2011
#
Last Modified: James @ Tuesday, April 5, 2011
#
#
This is a Python class that contains methods to make
#
calls to the MythTV frontend. It is designed to be
#
embedded in a C application.
#
#
Unless you have very good reason, this code SHOULD
#
NOT BE MODIFIED!
def main():
__init__()
# Constructor:
# This handles initializations; connecting to the
# frontend, and error-checking duties.
def __init__():
global frontend
global hostname
print "MythRCDriver started.."
print "MythRCDriver initializing.."
#The hostname of our respository that runs MythTV
hostname = 'mythtv'
#Initialization
MythLog._setlevel('none')
mythDatabase =
MythDB(args=(('DBHostName','localhost'),('DBName','mythconverg'),('DBUs
erName','mythtv'),('DBPassword','NZFrJZ6u')))
frontend = None
#Error checking
try:
frontend = mythDatabase.getFrontend(hostname)
frontend.connect()
print "Connected to frontend successfully!"
except socket.timeout:
print "Unable to connect to " + hostname + "."
pass
except TypeError:
print "Host " + hostname + " does not exist."
pass
except KeyboardInterrupt:
print "Keyboard interrupt reported."
sys.exit()
except:
print "Exception reported. The frontend may not be " \
"running, or the host name may be incorrect."
81
raise
# type: frontend.sendKey
# map: button 1 - record
def sendKeyRecord():
global frontend
frontend.sendKey('r')
# type: frontend.sendKey
# map: button 4 - info
def sendKeyInfo():
global frontend
frontend.sendKey('o')
# type: frontend.sendKey
# map: button 5 - up arrow
def sendKeyPageUp():
global frontend
frontend.sendKey('pageup')
# type: frontend.sendKey
# map: button 6 - mute
def sendKeyMute():
global frontend
frontend.sendKey('f9')
# type: frontend.sendPlay
# map: button 7 - channel up
def sendPlayChannelUp():
global frontend
frontend.sendPlay('channel up')
# type: frontend.sendKey
# map: button 8 - left arrow
def sendKeyLeft():
global frontend
frontend.sendKey('left')
# type: frontend.sendKey
# map: button 9 - enter (accept)
def sendKeyEnter():
global frontend
frontend.sendKey('enter')
# type: frontend.sendKey
# map: button 10 - right arrow
def sendKeyRight():
global frontend
frontend.sendKey('right')
# type: frontend.sendPlay
# map: button 11 - channel down
def sendPlayChannelDown():
global frontend
frontend.sendPlay('channel down')
# type: frontend.sendKey
# map: button 12 - previous chapter
def sendKeyPrevChapter():
global frontend
frontend.sendKey('pagedown')
82
# type: frontend.sendKey
# map: button 13 - down arrow
def sendKeyPageDown():
global frontend
frontend.sendKey('pagedown')
# type: frontend.sendKey
# map: button 14 - next chapter
def sendKeyNextChapter():
global frontend
frontend.sendKey('pageup')
# type: frontend.sendKey
# map: button 15 - volume up
def sendKeyVolumeUp():
global frontend
frontend.sendKey('f11')
# type: frontend.sendKey
# map: button 16 - key 1
def sendKeyButton1():
global frontend
frontend.sendKey('1')
# type: frontend.sendKey
# map: button 17 - key 2
def sendKeyButton2():
global frontend
frontend.sendKey('2')
# type: frontend.sendKey
# map: button 18 - key 3
def sendKeyButton3():
global frontend
frontend.sendKey('3')
# type: frontend.sendKey
# map: button 19 - volume down
def sendKeyVolumeDown():
global frontend
frontend.sendKey('f10')
# type: frontend.sendKey
# map: button 20 - key 4
def sendKeyButton4():
global frontend
frontend.sendKey('4')
# type: frontend.sendKey
# map: button 21 - key 5
def sendKeyButton5():
global frontend
frontend.sendKey('5')
# type: frontend.sendKey
# map: button 22 - key 6
def sendKeyButton6():
global frontend
frontend.sendKey('6')
83
# type: frontend.sendJump
# map: button 23 - play DVD
def jumpToPlayDVD():
global frontend
frontend.sendJump('playdvd')
# type: frontend.sendKey
# map: button 24 - key 7
def sendKeyButton7():
global frontend
frontend.sendKey('7')
# type: frontend.sendKey
# map: button 25 - key 8
def sendKeyButton8():
global frontend
frontend.sendKey('8')
# type: frontend.sendKey
# map: button 26 - key 9
def sendKeyButton9():
global frontend
frontend.sendKey('9')
# type: frontend.sendJump
# map: button 27 - live TV
def jumpToLiveTV():
global frontend
frontend.sendJump('livetv')
# type: frontend.sendKey
# map: button 28 - exit (escape)
def sendKeyEscape():
global frontend
frontend.sendKey('escape')
# type: frontend.sendKey
# map: button 29 - key 0
def sendKeyButton0():
global frontend
frontend.sendKey('0')
# type: frontend.sendKey
# map: button 30 - underscore
def sendKeyUnderscore():
global frontend
frontend.sendKey(‘_’)
# type: frontend.sendKey
# map: button 31 - rewind
def sendKeyRewind():
global frontend
frontend.sendKey('j')
# type: frontend.sendPlay
# map: button 32 - stop
def sendPlayStop():
global frontend
frontend.sendPlay('stop')
# type: frontend.sendKey
84
# map: button 33 - play
def sendKeyPlay():
global frontend
frontend.sendKey('p')
# type: frontend.sendPlay
# map: button 34 - pause
def sendPlayPause():
global frontend
frontend.sendPlay('speed pause')
# type: frontend.sendKey
# map: button 35 - fast forward
def sendKeyFastForward():
global frontend
frontend.sendKey('u')
# type: frontend.sendJump
# map: button 37 - myth main menu
def jumpToMainMenu():
global frontend
frontend.sendJump('mainmenu')
# type: frontend.sendJump
# map: button 38 - scheduling menu
def jumpToViewSchedule():
global frontend
frontend.sendJump('viewscheduled')
# type: frontend.sendJump
# map: button 39 - program guide
def jumpToProgramGuide():
global frontend
frontend.sendJump('programguide')
# testing method
def printerTest():
print "Success!"
main()
85
Appendix E: MythTVDriver.cpp Source Code
#include "MythTVDriver.h"
#include <Python.h>
#include <iostream>
int inputState = 0;
MythTVDriver::MythTVDriver()
{
pName = 0;
pModule = 0;
pDict = 0;
pFunc = 0;
Py_Initialize();
if( PyErr_Occurred() != NULL ) PyErr_Print();
//PyRun_SimpleString( "import sys" );
//PyRun_SimpleString( "sys.path.append(\"/usr/bin\")" );
pName = PyString_FromString( "MythRCDriver" );
if( PyErr_Occurred() != NULL ) PyErr_Print();
if( pName )
{
pModule = PyImport_Import( pName );
if( PyErr_Occurred() != NULL ) PyErr_Print();
if( pModule )
{
pDict = PyModule_GetDict( pModule );
if( PyErr_Occurred() != NULL ) PyErr_Print();
}
}
std::cout << "Driver Initialized" << std::endl;
}
MythTVDriver::~MythTVDriver()
{
if( pFunc )
{
Py_DECREF( pFunc );
}
if( pDict )
{
Py_DECREF( pDict );
}
if( pModule )
{
Py_DECREF( pModule );
}
if( pName )
{
Py_DECREF( pName );
}
Py_Finalize();
}
void MythTVDriver::sendKeyRecord()
{
if( pDict )
{
86
pFunc = PyDict_GetItemString( pDict, "sendKeyRecord" );
if( pFunc )
if( PyCallable_Check( pFunc ) )
PyObject_CallObject( pFunc, NULL );
}
}
void MythTVDriver::sendKeyApplicationPower()
{
system( "sudo -u myth -H start_myth.sh" );
}
void MythTVDriver::sendKeySystemPower()
{
/**
* James (April 5, 2011)
* Had to configure 'sudo visudo' in order to be able to run this
* without being prompted for root password. Added lines:
* user ALL=NOPASSWD: /usr/sbin/pm-hibernate
*/
system( "sudo pm-suspend" );
}
void MythTVDriver::sendKeyPageUp()
{
if( pDict )
{
pFunc = PyDict_GetItemString( pDict, "sendKeyPageUp" );
if( pFunc )
if( PyCallable_Check( pFunc ) )
PyObject_CallObject( pFunc, NULL );
}
}
void MythTVDriver::sendKeyMute()
{
if( pDict )
{
pFunc = PyDict_GetItemString( pDict, "sendKeyMute" );
if( pFunc )
if( PyCallable_Check( pFunc ) )
PyObject_CallObject( pFunc, NULL );
}
}
void MythTVDriver::sendPlayChannelUp()
{
if( pDict )
{
pFunc = PyDict_GetItemString( pDict, "sendPlayChannelUp" );
if( pFunc )
if( PyCallable_Check( pFunc ) )
PyObject_CallObject( pFunc, NULL );
}
}
void MythTVDriver::sendKeyLeft()
{
if( pDict )
{
pFunc = PyDict_GetItemString( pDict, "sendKeyLeft" );
87
if( pFunc )
if( PyCallable_Check( pFunc ) )
PyObject_CallObject(pFunc, NULL);
}
}
void MythTVDriver::sendKeyInfo()
{
if( pDict )
{
pFunc = PyDict_GetItemString( pDict, "sendKeyInfo" );
if( pFunc )
if( PyCallable_Check( pFunc ) )
PyObject_CallObject( pFunc, NULL );
}
}
void MythTVDriver::sendKeyRight()
{
if( pDict )
{
pFunc = PyDict_GetItemString( pDict, "sendKeyRight" );
if( pFunc )
if( PyCallable_Check( pFunc ) )
PyObject_CallObject( pFunc, NULL );
}
}
void MythTVDriver::sendPlayChannelDown()
{
if( pDict )
{
pFunc = PyDict_GetItemString( pDict, "sendPlayChannelDown" );
if( pFunc )
if( PyCallable_Check( pFunc ) )
PyObject_CallObject( pFunc, NULL );
}
}
void MythTVDriver::sendKeyPrevChapter()
{
if( pDict )
{
pFunc = PyDict_GetItemString( pDict, "sendKeyPrevChapter" );
if( pFunc )
if( PyCallable_Check( pFunc ) )
PyObject_CallObject( pFunc, NULL );
}
}
void MythTVDriver::sendKeyPageDown()
{
if( pDict )
{
pFunc = PyDict_GetItemString( pDict, "sendKeyPrevChapter" );
if( pFunc )
if( PyCallable_Check( pFunc ) )
PyObject_CallObject( pFunc, NULL );
}
}
88
void MythTVDriver::sendKeyNextChapter()
{
if( pDict )
{
pFunc = PyDict_GetItemString( pDict, "sendKeyNextChapter" );
if( pFunc )
if( PyCallable_Check( pFunc ) )
PyObject_CallObject( pFunc, NULL );
}
}
void MythTVDriver::sendKeyVolumeUp()
{
if( pDict )
{
pFunc = PyDict_GetItemString( pDict, "sendKeyVolumeUp" );
if( pFunc )
if( PyCallable_Check( pFunc ) )
PyObject_CallObject( pFunc, NULL );
}
}
void MythTVDriver::sendKey1()
{
if( pDict )
{
pFunc = PyDict_GetItemString( pDict, "sendKeyButton1" );
if( pFunc )
if( PyCallable_Check( pFunc ) )
PyObject_CallObject( pFunc, NULL );
}
}
void MythTVDriver::sendKey2()
{
if( pDict )
{
pFunc = PyDict_GetItemString( pDict, "sendKeyButton2" );
if( pFunc )
if( PyCallable_Check( pFunc ) )
PyObject_CallObject( pFunc, NULL );
}
}
void MythTVDriver::sendKey3()
{
if( pDict )
{
pFunc = PyDict_GetItemString( pDict, "sendKeyButton3" );
if( pFunc )
if( PyCallable_Check( pFunc ) )
PyObject_CallObject( pFunc, NULL );
}
}
void MythTVDriver::sendKey4()
{
if( pDict )
{
pFunc = PyDict_GetItemString( pDict, "sendKeyButton4" );
if( pFunc )
89
if( PyCallable_Check( pFunc ) )
PyObject_CallObject( pFunc, NULL );
}
}
void MythTVDriver::sendKey5()
{
if( pDict )
{
pFunc = PyDict_GetItemString( pDict, "sendKeyButton5" );
if( pFunc )
if( PyCallable_Check( pFunc ) )
PyObject_CallObject( pFunc, NULL );
}
}
void MythTVDriver::sendKey6()
{
if( pDict )
{
pFunc = PyDict_GetItemString( pDict, "sendKeyButton6" );
if( pFunc )
if( PyCallable_Check( pFunc ) )
PyObject_CallObject( pFunc, NULL );
}
}
void MythTVDriver::sendKey7()
{
if( pDict )
{
pFunc = PyDict_GetItemString( pDict, "sendKeyButton7" );
if( pFunc )
if( PyCallable_Check( pFunc ) )
PyObject_CallObject( pFunc, NULL );
}
}
void MythTVDriver::sendKey8()
{
if( pDict )
{
pFunc = PyDict_GetItemString( pDict, "sendKeyButton8" );
if( pFunc )
if( PyCallable_Check( pFunc ) )
PyObject_CallObject( pFunc, NULL );
}
}
void MythTVDriver::sendKey9()
{
if( pDict )
{
pFunc = PyDict_GetItemString( pDict, "sendKeyButton9" );
if( pFunc )
if( PyCallable_Check( pFunc ) )
PyObject_CallObject( pFunc, NULL );
}
}
void MythTVDriver::sendKey0()
90
{
if( pDict )
{
pFunc = PyDict_GetItemString( pDict, "sendKeyButton0" );
if( pFunc )
if( PyCallable_Check( pFunc ) )
PyObject_CallObject( pFunc, NULL );
}
}
void MythTVDriver::sendKeyUnderscore()
{
if( pDict )
{
pFunc = PyDict_GetItemString( pDict, "sendKeyUnderscore" );
if( pFunc )
if( PyCallable_Check( pFunc ) )
PyObject_CallObject( pFunc, NULL );
}
}
void MythTVDriver::sendKeyVolumeDown()
{
if( pDict )
{
pFunc = PyDict_GetItemString( pDict, "sendKeyVolumeDown" );
if( pFunc )
if( PyCallable_Check( pFunc ) )
PyObject_CallObject( pFunc, NULL );
}
}
void MythTVDriver::jumpToPlayDVD()
{
if( pDict )
{
pFunc = PyDict_GetItemString( pDict, "jumpToPlayDVD" );
if( pFunc )
if( PyCallable_Check( pFunc ) )
PyObject_CallObject( pFunc, NULL );
}
}
void MythTVDriver::jumpToLiveTV()
{
if( pDict )
{
pFunc = PyDict_GetItemString( pDict, "jumpToLiveTV" );
if( pFunc )
if( PyCallable_Check( pFunc ) )
PyObject_CallObject( pFunc, NULL );
}
}
void MythTVDriver::sendKeyEscape()
{
if( pDict )
{
pFunc = PyDict_GetItemString( pDict, "sendKeyEscape" );
if( pFunc )
if( PyCallable_Check( pFunc ) )
91
PyObject_CallObject( pFunc, NULL );
}
}
void MythTVDriver::sendKeyEnter()
{
if( pDict )
{
pFunc = PyDict_GetItemString( pDict, "sendKeyEnter" );
if( pFunc )
if( PyCallable_Check( pFunc ) )
PyObject_CallObject( pFunc, NULL );
}
}
void MythTVDriver::sendKeyRewind()
{
if( pDict )
{
pFunc = PyDict_GetItemString( pDict, "sendKeyRewind" );
if( pFunc )
if( PyCallable_Check( pFunc ) )
PyObject_CallObject( pFunc, NULL );
}
}
void MythTVDriver::sendPlayStop()
{
if( pDict )
{
pFunc = PyDict_GetItemString( pDict, "sendPlayStop" );
if( pFunc )
if( PyCallable_Check( pFunc ) )
PyObject_CallObject( pFunc, NULL );
}
}
void MythTVDriver::sendKeyPlay()
{
if( pDict )
{
pFunc = PyDict_GetItemString( pDict, "sendKeyPageUp" );
if( pFunc )
if( PyCallable_Check( pFunc ) )
PyObject_CallObject( pFunc, NULL );
}
}
void MythTVDriver::sendPlayPause()
{
if( pDict )
{
pFunc = PyDict_GetItemString( pDict, "sendPlayPause" );
if( pFunc )
if( PyCallable_Check( pFunc ) )
PyObject_CallObject( pFunc, NULL );
}
}
void MythTVDriver::sendKeyFastForward()
{
92
if( pDict )
{
pFunc = PyDict_GetItemString( pDict, "sendKeyFastForward" );
if( pFunc )
if( PyCallable_Check( pFunc ) )
PyObject_CallObject( pFunc, NULL );
}
}
void MythTVDriver::sendKeyInputSource()
{
if(inputState == 0)
{
jumpToLiveTV();
inputState = 1;
}
else if(inputState == 1)
{
jumpToPlayDVD();
inputState = 0;
}
}
void MythTVDriver::jumpToMainMenu()
{
if( pDict )
{
pFunc = PyDict_GetItemString( pDict, "jumpToMainMenu" );
if( pFunc )
if( PyCallable_Check( pFunc ) )
PyObject_CallObject( pFunc, NULL );
}
}
void MythTVDriver::jumpToViewSchedule()
{
if( pDict )
{
pFunc = PyDict_GetItemString( pDict, "jumpToViewSchedule" );
if( pFunc )
if( PyCallable_Check( pFunc ) )
PyObject_CallObject( pFunc, NULL );
}
}
void MythTVDriver::jumpToProgramGuide()
{
if( pDict )
{
pFunc = PyDict_GetItemString( pDict, "jumpToProgramGuide" );
if( pFunc )
if( PyCallable_Check( pFunc ) )
PyObject_CallObject( pFunc, NULL );
}
}
void MythTVDriver::receiveData( uint command )
{
switch( command )
{
case 0b111000001:
93
std::cout << "Function: Record" << std::endl;
sendKeyRecord();
break;
case 0b111000010:
std::cout << "Function: App Power" << std::endl;
sendKeyApplicationPower();
break;
case 0b111000011:
std::cout << "Function: Sys Power" << std::endl;
sendKeySystemPower();
break;
case 0b111000100:
std::cout << "Function: Info" << std::endl;
sendKeyInfo();
break;
case 0b111000101:
std::cout << "Function: Up Arrow" << std::endl;
sendKeyPageUp();
break;
case 0b111000110:
std::cout << "Function: Mute" << std::endl;
sendKeyMute();
break;
case 0b111000111:
std::cout << "Function: Channel Up" << std::endl;
sendPlayChannelUp();
break;
case 0b111001000:
std::cout << "Function: Left Arrow" << std::endl;
sendKeyLeft();
break;
case 0b111001001:
std::cout << "Function: Enter" << std::endl;
sendKeyEnter();
break;
case 0b111001010:
std::cout << "Function: Right Arrow" << std::endl;
sendKeyRight();
break;
case 0b111001011:
std::cout << "Function: Channel Down" << std::endl;
sendPlayChannelDown();
break;
case 0b111001100:
std::cout << "Function: Chapter Back" << std::endl;
sendKeyPageDown();
break;
case 0b111001101:
std::cout << "Function: Down Arrow" << std::endl;
sendKeyPageDown();
break;
case 0b111001110:
std::cout << "Function: Chapter Forward" << std::endl;
sendKeyPageUp();
break;
case 0b111001111:
std::cout << "Function: Volume Up" << std::endl;
sendKeyVolumeUp();
break;
case 0b111010000:
std::cout << "Function: 1" << std::endl;
94
sendKey1();
break;
case 0b111010001:
std::cout << "Function:
sendKey2();
break;
case 0b111010010:
std::cout << "Function:
sendKey3();
break;
case 0b111010011:
std::cout << "Function:
sendKeyVolumeDown();
break;
case 0b111010100:
std::cout << "Function:
sendKey4();
break;
case 0b111010101:
std::cout << "Function:
sendKey5();
break;
case 0b111010110:
std::cout << "Function:
sendKey6();
break;
case 0b111010111:
std::cout << "Function:
jumpToPlayDVD();
break;
case 0b111011000:
std::cout << "Function:
sendKey7();
break;
case 0b111011001:
std::cout << "Function:
sendKey8();
break;
case 0b111011010:
std::cout << "Function:
sendKey9();
break;
case 0b111011011:
std::cout << "Function:
jumpToLiveTV();
break;
case 0b111011100:
std::cout << "Function:
sendKeyEscape();
break;
case 0b111011101:
std::cout << "Function:
sendKey0();
break;
case 0b111011110:
std::cout << "Function:
sendKeyUnderscore();
break;
case 0b111011111:
std::cout << "Function:
sendKeyRewind();
2" << std::endl;
3" << std::endl;
Volume Down" << std::endl;
4" << std::endl;
5" << std::endl;
6" << std::endl;
Play DVD" << std::endl;
7" << std::endl;
8" << std::endl;
9" << std::endl;
Live TV" << std::endl;
Escape" << std::endl;
0" << std::endl;
Underscore" << std::endl;
Rewind" << std::endl;
95
break;
case 0b111100000:
std::cout << "Function: Stop" << std::endl;
sendPlayStop();
break;
case 0b111100001:
std::cout << "Function: Play" << std::endl;
sendKeyPlay();
break;
case 0b111100010:
std::cout << "Function: Pause" << std::endl;
sendPlayPause();
break;
case 0b111100011:
std::cout << "Function: Fast Forward" << std::endl;
sendKeyFastForward();
break;
case 0b111100100:
std::cout << "Function: Switch Input" << std::endl;
sendKeyInputSource();
break;
case 0b111100101:
std::cout << "Function: Main Menu" << std::endl;
jumpToMainMenu();
break;
case 0b111100110:
std::cout << "Function: View Schedule" << std::endl;
jumpToViewSchedule();
break;
case 0b111100111:
std::cout << "Function: Program Guide" << std::endl;
jumpToProgramGuide();
break;
default:
std::cout << "Unknown Function" << std::endl;
}
}
int main( int argc, const char* argv[] )
{
uint command = 0b111001010;
receiveData( &command );
}
void receiveData( uint *command )
{
std::cout << "receiveData(): You passed me " << *command << std::endl;
MythTVDriver *mtd = new MythTVDriver();
mtd->receiveData( *command );
delete mtd;
std::cout << "leaving receiveData()" << std::endl;
}
96
Appendix F: mythremotedriver.c Source Code
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
"mythremotedriver.h"
<libusb-1.0/libusb.h>
<errno.h>
<signal.h>
<string.h>
<stdio.h>
<stdlib.h>
<assert.h> // for assert()
<unistd.h> // for sleep()
<sys/types.h>
<sys/stat.h>
<fcntl.h>
<syslog.h>
<dlfcn.h>
<pthread.h> // for POSIX threads
// Setup Endpoints - The endpoints (2,4) are particular to the USBconnected device
#define EP_COMMAND
(0x01 | LIBUSB_ENDPOINT_OUT) // OUT of PC to USBdevice //DEVICE_SPECIFIC
#define EP_RESPONSE
(0x81 | LIBUSB_ENDPOINT_IN ) // IN PC from USBdevice //DEVICE_SPECIFIC
#define DEBUG_PRINT printf("at line %d\n", __LINE__ );
int do_exit = 0;
int logDebug = 0;
#define MYTHLIBRARY_PATH "/usr/lib/libMythTVDriver.so.1"
#define VENDOR_ID 0x04D8
void *libHandle;
double ( *function )( uint * );
int loadLibMythTVDriver()
{
char *error;
if( logDebug )
printf( "Attempting to load libMythTVDriver.\n" );
libHandle = dlopen( MYTHLIBRARY_PATH, RTLD_LAZY );
if( !libHandle )
{
printf( "Failed to load libMythTVDriver.\n" );
return 0;
}
if( logDebug )
printf( "Looking for function receiveData().\n" );
function = dlsym( libHandle, "receiveData" );
if( ( error = dlerror() ) != NULL )
{
printf( "Could not find function receiveData().\n");
return 0;
}
if( logDebug )
printf( "Successfully loaded libMythTVDriver.\n" );
97
return 1;
}
int initializeLibUSB()
{
if( logDebug )
printf( "Attempting to initialize libUSB.\n" );
int libusb_init_result = libusb_init( NULL );
if( libusb_init_result < 0 )
{
printf( "Failed to initialize libUSB.\n" );
return 0;
}
if( logDebug )
printf( "Successfully initialized libUSB.\n" );
return 1;
}
void connectToDevice()
{
// Discover devices
libusb_device **list;
libusb_device *foundDevice = NULL;
libusb_device_handle *handle = NULL;
struct libusb_device_descriptor desc;
size_t count = libusb_get_device_list( NULL, &list );
size_t i;
//First find the device
if( count > 0 )
{
for( i = 0; i < count; ++i )
{
libusb_device *device = list[ i ];
if( !libusb_get_device_descriptor( device, &desc ) )
{
if( VENDOR_ID == desc.idVendor )
{
foundDevice = device;
if( logDebug )
printf( "Found device.\n" );
break;
}
}
}
}
else if( logDebug )
printf( "libUSB found no USB devices.\n" );
//Now try to open and interface with it
if( foundDevice )
{
if( libusb_open( foundDevice, &handle ) ) //Couldn't open
{
98
if( logDebug )
printf( "Could not open device.\n" );
}
else //Could open
{
if( libusb_claim_interface( handle, 0 ) ) //Couldn't interface
{
if( logDebug )
printf( "Could not claim device interface.\n" );
}
else //Could interface
device_handle = handle;
}
}
else
if( logDebug )
printf( "Device not found.\n" );
libusb_free_device_list( list, 1 );
if( !device_handle )
libUSBCleanup();
}
void libUSBCleanup()
{
if( device_handle )
{
libusb_release_interface( device_handle, 0 );
libusb_close( device_handle );
device_handle = 0;
}
libusb_exit( NULL );
}
void freeTransfers()
{
if( transfer_command_out )
{
libusb_free_transfer( transfer_command_out );
transfer_command_out = 0;
}
if( transfer_response_in )
{
libusb_free_transfer( transfer_response_in );
transfer_response_in = 0;
}
}
void cancelTransfers()
{
if( transfer_command_out )
libusb_cancel_transfer( transfer_command_out );
if( transfer_response_in )
libusb_cancel_transfer( transfer_response_in );
}
void *sendToMyth( void *message )
{
99
uint iMessage = ( uint ) message;
( *function )( &iMessage );
//James: This thread is finished with it's work.
pthread_exit( NULL );
}
/**
* Main function
*/
int main( void )
{
/* This code is important for writing a dameon. We want the calling
thread to not block on us. */
pid_t parentID;
pid_t childID;
parentID = fork();
if( parentID < 0 )
exit( EXIT_FAILURE );
if( parentID > 0 )
exit( EXIT_SUCCESS );
childID = setsid();
if( childID < 0 )
exit( EXIT_FAILURE );
/* Now we can start */
if( logDebug )
printf( "MythTV Remote USB driver started.\n" );
if( !loadLibMythTVDriver() )
return -1;
if( !initializeLibUSB() )
return -2;
connectToDevice();
if( !device_handle )
return -3;
//James: threading declarations
int threadStatus = 0;
pthread_t createdThread;
// This is the array that holds data sent from the remote
int i;
char data_string[ 10 ]; // Bigger than 10, just in case
for( i = 0; i < 10; ++i )
data_string[ i ] = '0';
// This is the buffer that holds data to send to the remote
unsigned char *buf = malloc( 4 );
// These next 4 values send command to device for it to send back data.
// Brent - Do we need to send 4 of these?
buf[ 0 ] = 0x81; //DEVICE_SPECIFIC
buf[ 1 ] = 0x81; //DEVICE_SPECIFIC
buf[ 2 ] = 0x81; //DEVICE_SPECIFIC
buf[ 3 ] = 0x81; //DEVICE_SPECIFIC
100
int libusb_submit_transfer_in_result;
int libusb_submit_transfer_out_result;
// Define transfer of data IN (IN to host PC from USB-device)
transfer_response_in = libusb_alloc_transfer( 32 );
libusb_fill_bulk_transfer( transfer_response_in, device_handle,
EP_RESPONSE, ( unsigned char * ) data_string,
sizeof( data_string ), cb_response_in, NULL, 100 );
libusb_submit_transfer_in_result = libusb_submit_transfer(
transfer_response_in );
if( !libusb_submit_transfer_in_result )
{
if( logDebug )
printf( "Successful transfer libusb_submit_transfer(
transfer_response_in ).\n" );
}
else
{
printf( "Error: Return value from libusb_submit_transfer(
transfer_response_in ) = %d.\n",
libusb_submit_transfer_in_result );
}
// Define transfer of data OUT (OUT to USB-device from host PC)
transfer_command_out = libusb_alloc_transfer( 32 ); //Allocation
returns struct libusb_transfer*
libusb_fill_bulk_transfer( transfer_command_out, device_handle,
EP_COMMAND, buf, sizeof( buf ), cb_command_out, NULL, 0 );
libusb_submit_transfer_out_result = libusb_submit_transfer(
transfer_command_out );
if( !libusb_submit_transfer_out_result )
{
if( logDebug )
printf( "Successful transfer libusb_submit_transfer(
transfer_command_out ).\n" );
}
else
{
printf( "Error: Return value from libusb_submit_transfer(
transfer_command_out ) = %d.\n", libusb_submit_transfer_out_result );
}
//Brent - I don't really like this...
struct sigaction sigact;
// Define signal handler to catch system generated signals
// (If user hits CTRL+C, this will deal with it.)
sigact.sa_handler = sighandler; // sighandler is defined below. It
just sets do_exit.
sigemptyset( &sigact.sa_mask );
sigact.sa_flags = 0;
sigaction( SIGINT, &sigact, NULL );
sigaction( SIGTERM, &sigact, NULL );
sigaction( SIGQUIT, &sigact, NULL );
if( logDebug )
printf( "About to enter infinite loop...\n" );
while( !do_exit )
{
101
if( logDebug )
printf( "And loop...\n" );
struct timeval tv;
tv.tv_sec = 0;
// 0 s
tv.tv_usec = 100000;
// 100 ms
if( libusb_handle_events_timeout( NULL, &tv ) )
{
freeTransfers();
libUSBCleanup();
if( logDebug )
printf( "Timed out.\n" );
return -4;
}
usleep( 100000 );
libusb_fill_bulk_transfer( transfer_command_out, device_handle,
EP_COMMAND, buf, sizeof( buf ), cb_command_out, NULL, 0 );
libusb_submit_transfer( transfer_command_out );
libusb_fill_bulk_transfer( transfer_response_in, device_handle,
EP_RESPONSE, ( unsigned char * ) data_string, sizeof( data_string ),
cb_response_in, NULL, 100 );
libusb_submit_transfer( transfer_response_in );
if( logDebug )
printf( "Transfers complete.\n" );
//Immediately filter out bad data
if( transfer_response_in->buffer[ 1 ] == 5 )
continue;
//Alright. Bit bang example 101.
uint remoteCode = transfer_response_in->buffer[ 1 ];
int j;
for( j = 2; j < 10; ++j )
{
remoteCode = remoteCode << 1;
remoteCode = remoteCode | transfer_response_in->buffer[ j ];
}
if( logDebug )
printf( "Remote code %x \n", remoteCode );
if( remoteCode > 0b111000000 )
{
if(logDebug)
{
printf("About to create thread..\n");
}
//sendToMyth(remoteCode);
//James: call sendToMyth as an individual thread
threadStatus = pthread_create( &createdThread, NULL,
sendToMyth, ( void * ) remoteCode );
if(threadStatus)
{
printf( "ERROR: return code from pthread_create() is %d\n",
threadStatus );
}
102
else
{
printf( "Thread created, call made.\n" );
}
threadStatus = pthread_detach( createdThread );
}
for( j = 0; j < 10; ++j )
{
transfer_response_in->buffer[ j ] = 0;
}
}
cancelTransfers();
freeTransfers();
libUSBCleanup();
dlclose( libHandle );
//Brent - Why are we not cleaning up ANYTHING?
free( buf );
return 0;
}
void cb_command_out( struct libusb_transfer *transfer )
{
libusb_transfer( transfer );
if( transfer->status != LIBUSB_TRANSFER_COMPLETED )
{
if( logDebug )
printf( "Could not send command to receiver: %d.\n", transfer>status );
libusb_free_transfer( transfer );
transfer_command_out = NULL;
}
}
void cb_response_in( struct libusb_transfer *transfer )
{
libusb_transfer( transfer );
if( transfer->status == LIBUSB_TRANSFER_TIMED_OUT )
{
if( logDebug )
printf( "Timed out while receiving data from receiver.\n" );
libusb_free_transfer( transfer );
transfer_response_in = NULL;
}
}
// This will catch user initiated CTRL+C type events and allow the program
to exit
void sighandler( int signum )
{
if( logDebug )
printf( "sighandler\n" );
do_exit = 1;
}
// debugging function to display libusb_transfer
103
void libusb_transfer( struct libusb_transfer *p_t )
{
if( p_t )
{
if( logDebug )
{
printf("libusb_transfer structure\n");
printf("flags
=%x \n", p_t->flags);
printf("endpoint=%x \n", p_t->endpoint);
printf("type
=%x \n", p_t->type);
printf("timeout =%d \n", p_t->timeout);
// length, and buffer are commands sent to scanner
printf("length
=%d \n", p_t->length);
printf("actual_length =%d \n", p_t->actual_length);
printf("buffer
=%p \n", p_t->buffer);
printf("status
=%d \n", p_t->status);
int i;
for( i=0; i < p_t->length; i++ )
{
printf("buffer[%d] =%x \n", i, p_t->buffer[i]);
}
}
}
}
104