Difference between revisions of "S15: Remote Learner"
Proj user11 (talk | contribs) (→Hardware Interface) |
Proj user11 (talk | contribs) (→Android App Testing) |
||
(63 intermediate revisions by 2 users not shown) | |||
Line 1: | Line 1: | ||
== Remote Learner == | == Remote Learner == | ||
+ | |||
+ | [[File:S15_244_G3_IMG_0001.jpg | 500px | left | thumb | Finished Product view1]] | ||
+ | [[File:S15_244_G3_IMG_0002.jpg | 500px | right | thumb | Finished Product view2]] | ||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
== Abstract == | == Abstract == | ||
Line 5: | Line 50: | ||
== Objectives & Introduction == | == Objectives & Introduction == | ||
− | The objects for the remote learner are as the following: | + | Project Remote Learner is to have the ability to learn button functionalities from multiple remotes, and then control devices via an Android application. The objects for the remote learner are as the following: |
* To create an Android application that will communicate with SJOne board using Bluetooth and UART | * To create an Android application that will communicate with SJOne board using Bluetooth and UART | ||
* To create a task that will be signal by the Android application to learn the IR timing using the on-board IR receiver. | * To create a task that will be signal by the Android application to learn the IR timing using the on-board IR receiver. | ||
Line 12: | Line 57: | ||
=== Team Members & Responsibilities === | === Team Members & Responsibilities === | ||
− | * Bailey Wu - Android App and BT I/F | + | * Ying (Bailey) Wu - Android App and BT I/F |
* Yoni Klein - IR RX and IR Learning | * Yoni Klein - IR RX and IR Learning | ||
* Tejeswar - IR Protocol | * Tejeswar - IR Protocol | ||
Line 187: | Line 232: | ||
== Design & Implementation == | == Design & Implementation == | ||
=== Hardware Design === | === Hardware Design === | ||
− | + | [[File:S15_244_G3_IMG_003.jpg|600px|centre|thumb|System Block Diagram]] | |
− | ====Transmit IR LED | + | ====Transmit IR LED==== |
− | |||
The IR LED transmitter is a simple circuit that connects to a PWM pin that is set to toggle at 38 Khz. The circuit needs a resister to create the voltage drop for the IR LED. | The IR LED transmitter is a simple circuit that connects to a PWM pin that is set to toggle at 38 Khz. The circuit needs a resister to create the voltage drop for the IR LED. | ||
<br> | <br> | ||
− | [[File: | + | [[File:CmpE244_S15_T3_Remote_Learner_IR LED Circuit.png|600px|centre|thumb|IR LED circuit]] |
<br> | <br> | ||
− | ====Receive IR LED | + | |
− | + | ====Receive IR LED==== | |
− | The IR receiver is already part of the SJOne board. On our revision 4 board, this is the TSOP75238TR IR Receiver Module by Vishay Semiconductors. Other revision boards may have a slightly different part, but the operation and concepts are the same. | + | The IR receiver is already part of the SJOne board. On our revision 4 board, this is the TSOP75238TR 38KHz IR Receiver Module by Vishay Semiconductors. Other revision boards may have a slightly different part, but the operation and concepts are the same. |
[[File:S15_244_RemoteLearner_IR_Receiver.png|400px|centre|thumb|IR Receiver Circuit]] | [[File:S15_244_RemoteLearner_IR_Receiver.png|400px|centre|thumb|IR Receiver Circuit]] | ||
<br> | <br> | ||
Line 210: | Line 254: | ||
[[File:S15_244_RemoteLearner_IR_Receiver_Test_Pin.png|365px|centre|thumb|External Pin]] | [[File:S15_244_RemoteLearner_IR_Receiver_Test_Pin.png|365px|centre|thumb|External Pin]] | ||
+ | |||
+ | ====Android Application with SJOne Board Hardware Design ==== | ||
+ | The Android application hardware design consists of the Bluetooth module (HC-06) and SJOne board as shown in the figure below. The communication link is established using the SJOne board UART3 Tx and Rx Pins and the HC-06 Bluetooth module. Messages are being sent in a hand shake manner. Once the SJOne board received an command, the transmit and receive tasks would act accordingly. After the operations are completed, it would notify the Android application the result of the requested command. | ||
+ | |||
+ | {| style="width:800px;" | | ||
+ | |[[File:CMPE244_S15_TEAM2_Android_Block_Diagram.jpg|600px|center|thumb|Android Hardware Design: System Block Diagram]] | ||
+ | |} | ||
=== Hardware Interface === | === Hardware Interface === | ||
In this section, you can describe how your hardware communicates, such as which BUSes used. You can discuss your driver implementation here, such that the '''Software Design''' section is isolated to talk about high level workings rather than inner working of your project. | In this section, you can describe how your hardware communicates, such as which BUSes used. You can discuss your driver implementation here, such that the '''Software Design''' section is isolated to talk about high level workings rather than inner working of your project. | ||
− | + | ====PWM to IR Transmitter LED==== | |
− | ====PWM to IR Transmitter LED | ||
− | |||
The PWM was used to generate the 38 Khz by creating an instance of PWM class. Then using the predefined function of "pwm.set" to toggle the pin at a duty cycle of about 25% and after certain amount of time we set the duty cycle to 0. Below is the standard behavior for NEC IR protocol explaining the amount of time that the PWM pin needs to toggle and time required to be off. | The PWM was used to generate the 38 Khz by creating an instance of PWM class. Then using the predefined function of "pwm.set" to toggle the pin at a duty cycle of about 25% and after certain amount of time we set the duty cycle to 0. Below is the standard behavior for NEC IR protocol explaining the amount of time that the PWM pin needs to toggle and time required to be off. | ||
Line 247: | Line 296: | ||
|} | |} | ||
− | [[File: | + | [[File:CmpE244_S15_T3_Remote_Learner_NEC_IR.JPG|600px|centre|thumb|In the above image is a capture of a NEC IR transmission using an oscilloscope.]] |
<br> | <br> | ||
− | + | ====IR Receiver to Capture Pin==== | |
The IR receiver outputs a logic one (high, 3.3v) signal when idle. When active, the signal drops to a logic zero (low, 0v). Therefore, the signal is active low, which may actually be inverted relative to the transmitted signal. The images below show two examples of transmitted signals and receiver output. | The IR receiver outputs a logic one (high, 3.3v) signal when idle. When active, the signal drops to a logic zero (low, 0v). Therefore, the signal is active low, which may actually be inverted relative to the transmitted signal. The images below show two examples of transmitted signals and receiver output. | ||
Line 265: | Line 314: | ||
=== Software Design === | === Software Design === | ||
− | |||
− | ====IR Receive Task | + | ====IR Receive Task==== |
+ | |||
+ | There are two components that make the IR receive functionality work. The first is the task itself, which processes commands from the Bluetooth task and saves IR timings to the SD card. | ||
+ | |||
+ | ''The receive task and capture code was modified from Preet's IR_Sensor and remoteTask classes. This was a great starting place but needed to be modified to suit the project needs.'' | ||
+ | This task sleeps indefinitely until it receives a message on the mRXQueue. If this message contains a learn command, it will attempt to take the mutex shared by the transmit task. If it can take it, that means the system is not transmitting and it's safe to start recording timing signals. If it can't take it, then the task will let the Bluetooth task know the system is busy and cannot learn new commands. | ||
+ | However, if learning can continue, we signal the second part of the IR receive functionality. We tell the IR_Sensor singleton class to clear any stored captures and wait until a new command is received or a timeout occurs. Next, we retrieve the array of timings and store it on the SD card for the transmit task to use. | ||
+ | The code segment and flow chart to show this behavior are shown below. | ||
+ | <br> | ||
+ | <syntaxhighlight lang="c"> | ||
+ | bool remoteRXTask::run(void *p) | ||
+ | { | ||
+ | u0_dbg_printf("Learning...\n" ); | ||
+ | //Receive item from queue | ||
+ | xQueueReceive(rxQueue); | ||
+ | //Check if it is a learn command and semaphore avaliable | ||
+ | if ((learn_command) && (xSemaphoreAvaliable)) | ||
+ | { | ||
+ | //Read IR code | ||
+ | IS.clearCode(); | ||
+ | while(!IS.isIRCodeReceived()) | ||
+ | { | ||
+ | vTaskDelayMs(100); | ||
+ | } | ||
+ | uint8_t max_timings = 255; | ||
+ | uint16_t timings[max_timings] = {0}; | ||
+ | uint16_t count; | ||
+ | IS.getLastIRCode(timings, &count, max_timings); | ||
+ | //get+store the code and send feedback here | ||
+ | sprintf(filename,"1:%c.hex",mMsg.button); | ||
+ | Storage :: write(filename,(void *)&timings,count,0); | ||
+ | xSemaphoreGive(mIRMutex); | ||
+ | } | ||
+ | else | ||
+ | { | ||
+ | strcpy(mMsg.msg,"*****Busy Transmitting***"); | ||
+ | //Send item to the Bluetooth queue | ||
+ | xQueueSend(mBtQueue,&mMsg,0); | ||
+ | } | ||
+ | return true; | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | <br> | ||
+ | [[File:S15_244_RemoteLearner_IR_Receiver_TaskFlow.png|700px|center|Flow Diagram for IR receive Task|thumb|Flow Chart : IR Receive Task]] | ||
+ | <br> | ||
+ | The code below is the core of what makes the IR learning possible. If a rising or falling edge occurs on the capture pin, then an interrupt is generated. In this case, storeIrCode() will be called which saves the timestamps to an array. We set a timeout of 50ms, after which decodeIrCode() is called that calculates the differences in timestamps. These differences are the timings we will store and later transmit. | ||
+ | |||
+ | The 50ms timeout was chosen because some remotes have more complicated commands that include two consecutive commands. The time between those commands was measured to be about 40ms with the tested remotes. | ||
+ | <syntaxhighlight lang="c"> | ||
+ | #if (1 == SYS_CFG_SYS_TIMER) | ||
+ | /* ISR for captured time of the capture input pin */ | ||
+ | if (intr_reason & timer_capt0_intr_ir_sensor_edge_time_captured) | ||
+ | { | ||
+ | gp_timer_ptr->IR = timer_capt0_intr_ir_sensor_edge_time_captured; | ||
+ | // Store the IR capture time and setup timeout of the IR signal (unless we reset it again) | ||
+ | IS.storeIrCode(gp_timer_ptr->CR0); | ||
+ | //10k was too short of a timeout for long commands, | ||
+ | //which sometimes have separations of up to 40ms | ||
+ | gp_timer_ptr->MR2 = 50000 + gp_timer_ptr->TC; | ||
+ | } | ||
+ | /* MR2: End of IR capture (no IR capture after initial IR signal) */ | ||
+ | else if (intr_reason & timer_mr2_intr_ir_sensor_timeout) | ||
+ | { | ||
+ | gp_timer_ptr->IR = timer_mr2_intr_ir_sensor_timeout; | ||
+ | IS.decodeIrCode(); | ||
+ | } | ||
+ | /* MR0 is used for the timer rollover count */ | ||
+ | else | ||
+ | #endif | ||
+ | </syntaxhighlight> | ||
+ | <br> | ||
+ | <syntaxhighlight lang="c"> | ||
+ | void IR_Sensor::storeIrCode(uint32_t value) | ||
+ | { | ||
+ | if (!m_ready_to_receive) | ||
+ | return; | ||
+ | // Just store the timestamp of this signal | ||
+ | if(g_signal_count < MAX_EDGES_PER_IR_FRAME) | ||
+ | { | ||
+ | g_ir_edge_timings[g_signal_count++] = value; | ||
+ | } | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | <br> | ||
+ | |||
+ | <syntaxhighlight lang="c"> | ||
+ | void IR_Sensor::decodeIrCode(void) | ||
+ | { | ||
+ | if (!m_ready_to_receive) | ||
+ | return; | ||
+ | for (int i = 0; i < g_signal_count-1; i++) | ||
+ | { | ||
+ | g_ir_cmd_timings[i] = g_ir_edge_timings[i+1]-g_ir_edge_timings[i]; | ||
+ | } | ||
+ | m_code_received = true; | ||
+ | m_ready_to_receive = false; | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | <br> | ||
+ | |||
+ | [[File:S15_244_RemoteLearner_IR_Receiver_CaptureFlow.png|500px|center|Flow Diagram for IR capture|thumb|Flow Chart : IR Capture Interrupt]] | ||
====IR Transmit Task:==== | ====IR Transmit Task:==== | ||
Line 276: | Line 424: | ||
The IR transmit task will be signaled by the Bluetooth task. After being signal the task will attempt to read from a file using the Storage class. If the file doesn't exist it will send an error message the terminal. If the file does exist then the task will read all the timing values into an array. The task will then enter a PortCritical area and transmit the IR signal by turning on and off the PWM pin. Once it is finished the IR Transmit task return the Mutex. | The IR transmit task will be signaled by the Bluetooth task. After being signal the task will attempt to read from a file using the Storage class. If the file doesn't exist it will send an error message the terminal. If the file does exist then the task will read all the timing values into an array. The task will then enter a PortCritical area and transmit the IR signal by turning on and off the PWM pin. Once it is finished the IR Transmit task return the Mutex. | ||
<br> | <br> | ||
+ | <syntaxhighlight lang="c"> | ||
remoteTXTask(uint8_t priority) :scheduler_task("rTX", 2048, priority), pwm0(38Khz) | remoteTXTask(uint8_t priority) :scheduler_task("rTX", 2048, priority), pwm0(38Khz) | ||
bool run(void *p) | bool run(void *p) | ||
Line 297: | Line 446: | ||
return true; | return true; | ||
} | } | ||
− | [[File: | + | </syntaxhighlight> |
+ | [[File:CmpE244_S15_T3_Remote_Learner_IRTask.jpg|250px|center|Flow Diagram for IR transmit Task|350px|thumb|Flow Chart : IR Transmit Task]] | ||
====Android Application with SJOne Board Software Design ==== | ====Android Application with SJOne Board Software Design ==== | ||
− | [[File:CMPE244_S15_TEAM2_Android_FlowChart.png| | + | [[File:CMPE244_S15_TEAM2_Android_FlowChart.png|300px|right|thumb|Flow Chart: Android App with SJOne Board Software Design]] |
+ | |||
+ | The Android application Bluetooth task is to provide the a communication link between all the tasks. The Bluetooth task would receive commands from the application, then send items to the queue with the appropriate information such as command, button number, and message from the Android application. | ||
+ | |||
+ | <br> | ||
+ | '''Pseudo Code:''' | ||
+ | <syntaxhighlight lang="c"> | ||
+ | void BluetoothTask(void *p) { | ||
+ | //Initialize transmit, receive and bluetooth queues | ||
+ | bool init(); | ||
+ | bool run() | ||
+ | { | ||
+ | //Get command from Android app | ||
+ | uart3.gets(); | ||
+ | //Check if it is a transmit or learn command | ||
+ | checkCommand(); | ||
+ | //Send item to the appropriate queue | ||
+ | xQueueSend(); | ||
+ | //Sleep to reduce CPU usage | ||
+ | vTaskDelay(10); | ||
+ | } | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | <br><br><br><br> | ||
=== Remote Learner Android App === | === Remote Learner Android App === | ||
− | The software development environment for the Remote Learner | + | The software development environment for the Remote Learner Android application is Android Studio 1.2.1.1, it is the official IDE for Android application development, based on IntelliJ IDEA. The IDE simplified the way how Android application was developed traditionally. It comes with a built-in Android Emulator so there was no need additional plug-in or downloads. However, in the Remote Learner application, Android emulator was not very helpful because it requires an active Bluetooth connection. Hence, a real Android device was used throughout development. Figures below show the Remote Leaner Android application and its design layout. At the same time, it has the capability to receive status from the SJOne board after an command has been sent. |
[[File:CMPE244_S15_TEAM2_Android_AndroidStudio.png|900px|center|thumb|Android App: Development Envirnoment]] | [[File:CMPE244_S15_TEAM2_Android_AndroidStudio.png|900px|center|thumb|Android App: Development Envirnoment]] | ||
Line 310: | Line 484: | ||
<br/><br/> | <br/><br/> | ||
+ | The Remote Learner application initial screen is shown in the left figure below. It has the capability to pair up with an Bluetooth device, then transmit commands to the device. Under the application title, there is a status message that updates instantaneously from initiating and establishing Bluetooth connection requests. | ||
{| style="width:1200px;" | | {| style="width:1200px;" | | ||
Line 318: | Line 493: | ||
<br/><br/> | <br/><br/> | ||
+ | Once a Bluetooth connection has been established. The application would allow user to send button commands to the Bluetooth device. For example, to learn a button, the user would need to hold the button on the application, and wait for a reply message from the SJOne board Bluetooth task. Once a button is learned successfully, it would display a status message as shown in the middle figure below. If the request to learn a button failed, it would display a status message as shown in the right figure below. | ||
{| style="width:1200px;" | | {| style="width:1200px;" | | ||
|[[File:CMPE244_S15_TEAM2_Android_Connected.png|350px|left|thumb|Android App: Bluetooth Device Connected]] | |[[File:CMPE244_S15_TEAM2_Android_Connected.png|350px|left|thumb|Android App: Bluetooth Device Connected]] | ||
Line 327: | Line 503: | ||
== Testing & Technical Challenges == | == Testing & Technical Challenges == | ||
− | |||
− | |||
− | |||
− | |||
=== IR Transmit Issue #1 === | === IR Transmit Issue #1 === | ||
Line 358: | Line 530: | ||
=== Android App Testing === | === Android App Testing === | ||
− | + | The initial testing and debugging was used with the Android emulator. The built-in emulator allows real time debugging, it was very helpful for confirming simple events within the application. After Bluetooth chat service was implemented, a real Android device and logging was used for testing and debugging. | |
== Conclusion == | == Conclusion == | ||
− | + | Our team was successful in completing the Remote Learner project. We faced a few issues such as debugging the PWM frequency to the correct value and integrating the project together. Our team expanded our knowledge of using FreeRTOS to implement individual task and having them communicate with each other thru queue. We learned about the different protocols such as NEC that was used by some of the devices we tested. Lastly, we learn how to develop an Android application to control everything. | |
=== Project Video === | === Project Video === | ||
− | + | https://www.youtube.com/watch?v=9CvH5DOZiIU | |
=== Project Source Code === | === Project Source Code === |
Latest revision as of 08:15, 25 May 2015
Contents
Remote Learner
Abstract
The objective of this project was to create a remote learner using the SJOne board. The Remote Learner would have the ability to be learn any buttons of any remote controller then controlled by an Android application using Bluetooth. The application would tell the SJOne board to learn a remote control button using the onboard IR receiver sensor. Once the button was learned, then the SJOne board notifys the Android application and allows the application to replicate the button functionality using our own IR transmitter.
Objectives & Introduction
Project Remote Learner is to have the ability to learn button functionalities from multiple remotes, and then control devices via an Android application. The objects for the remote learner are as the following:
- To create an Android application that will communicate with SJOne board using Bluetooth and UART
- To create a task that will be signal by the Android application to learn the IR timing using the on-board IR receiver.
- Task will then send a message to Android application through a Queue to indicate if it was able to successfully capture and write the timing values to a file to Flash memory
- To create a task that will receive a signal to transmit the IR timing values from Flash memory. This task will use the PWM library and delay function to turn on and off the IR LED
Team Members & Responsibilities
- Ying (Bailey) Wu - Android App and BT I/F
- Yoni Klein - IR RX and IR Learning
- Tejeswar - IR Protocol
- Christopher Laurence - IR TX and IR learning
Schedule
Remote Learner
Week | Start Date | End Date | Task | Status | Actual Completion Date |
---|---|---|---|---|---|
1-2 | 3/27/2015 | 4/10/2015 | Research about the basic of remote controller protocols | Completed | 3/10/15 |
3 | 4/10/2015 | 4/17/2015 | Research about transmit timing of the protocol and analyze the data with a oscilloscope
Research about receive timing of the protocol and analyze the data with a oscilloscope |
Completed | 4/17/2015 |
4 | 4/17/2015 | 4/24/2015 | Accurately decode IR signals from a remote control
Accurately transmit IR signals to a device |
Completed | 5/2/2015 |
5 | 4/24/2015 | 5/1/2015 | Interface BT module and establish communication with the Android phone
Decide on a communication protocol between phone and board. |
Completed | 05/22/2015 |
6-7 | 5/1/2015 | 5/15/2015 | Research and add additional protocol support | Completed | 5/15/2015 |
8 | 5/15/2015 | 5/22/2015 | Final integration and testing. | Completed | 05/23/2015 |
Android Application
Week | Start Date | End Date | Task | Status | Actual Completion Date |
---|---|---|---|---|---|
1 | 4/6/2015 | 4/13/2015 | Research about Android application development
Research about Bluetooth module implementation with SJOne board |
Completed | 4/10/2015 |
2 | 4/13/2015 | 4/20/2015 | Implement and design a basic Android application on Android Studio
Implement the Bluetooth module using UART |
Completed | 4/27/2015 |
3 | 4/20/2015 | 4/27/2015 | Continue development of the Android application
Establish a connection between the SJOne board and Android application through Bluetooth |
Completed | 5/04/2015 |
4 | 4/27/2015 | 5/4/2015 | Collaborate with team members to integrate the Android application and remote learner functions | Completed | 5/11/2015 |
5 | 5/4/2015 | 5/11/2015 | Debug and troubleshoot any issues that may arise | Completed | 5/23/2015 |
6 | 5/11/2015 | 5/18/2015 | Miscellaneous | Completed | 5/23/2015 |
Parts List & Cost
Give a simple list of the cost of your project broken down by components. Do not write long stories here.
Line Item# | Part Desciption | Vendor | Part Number | Qty | Cost ($) |
---|---|---|---|---|---|
1 | 120 Resistor | Anchor Electronic | N/A | 10 | 1.00 |
2 | IR LED 1.5 V 40mA | Anchor Electronic | N/A | 4 | 1.00 |
3 | HC-06 Bluetooth Module | N/A | N/A | 1 | 9.99 |
4 | SJOne | N/A | N/A | 1 | 80.00 |
Design & Implementation
Hardware Design
Transmit IR LED
The IR LED transmitter is a simple circuit that connects to a PWM pin that is set to toggle at 38 Khz. The circuit needs a resister to create the voltage drop for the IR LED.
Receive IR LED
The IR receiver is already part of the SJOne board. On our revision 4 board, this is the TSOP75238TR 38KHz IR Receiver Module by Vishay Semiconductors. Other revision boards may have a slightly different part, but the operation and concepts are the same.
The basic circuit is relatively simple. Along with power and ground, an output signal is connected to two different parts of the board.
The first connection is made to P1[18] on the LPC controller. This GPIO pin will be configured to use the capture feature of the boards timer in order to "capture" the time a rising or falling signal event occurs. This pin is labeled as "IR_DATA" on the board schematic and the associated GPIO function is referred to as CAP1[0] in the user manual. The operation of the capture pin is described in the Hardware Interface section below.
The second connection is to an external pin referenced in section 6D on page 3 of the board schematic. This is mainly used for debugging and validating captured data on an oscilloscope. Thanks Preet for adding this! Both of these connections are displayed in the images below.
Android Application with SJOne Board Hardware Design
The Android application hardware design consists of the Bluetooth module (HC-06) and SJOne board as shown in the figure below. The communication link is established using the SJOne board UART3 Tx and Rx Pins and the HC-06 Bluetooth module. Messages are being sent in a hand shake manner. Once the SJOne board received an command, the transmit and receive tasks would act accordingly. After the operations are completed, it would notify the Android application the result of the requested command.
Hardware Interface
In this section, you can describe how your hardware communicates, such as which BUSes used. You can discuss your driver implementation here, such that the Software Design section is isolated to talk about high level workings rather than inner working of your project.
PWM to IR Transmitter LED
The PWM was used to generate the 38 Khz by creating an instance of PWM class. Then using the predefined function of "pwm.set" to toggle the pin at a duty cycle of about 25% and after certain amount of time we set the duty cycle to 0. Below is the standard behavior for NEC IR protocol explaining the amount of time that the PWM pin needs to toggle and time required to be off.
Protocol | On(µs) | Off(µs) | Total(µs) |
---|---|---|---|
Start | 9000 | 4500 | 13500 |
0 bit | 560 | 560 | 1120 |
1 bit | 560 | 1690 | 2250 |
End | 560 | N/A | 560 |
IR Receiver to Capture Pin
The IR receiver outputs a logic one (high, 3.3v) signal when idle. When active, the signal drops to a logic zero (low, 0v). Therefore, the signal is active low, which may actually be inverted relative to the transmitted signal. The images below show two examples of transmitted signals and receiver output.
These images are from the IR receiver data sheet included in the development package of the SJOne board. However, you can find it on Vishay's website as well. This signal is physically connected to one of the capture pins on the LPC microcontroller. In our example, the board is designed to take this input on P1[18] using CAP1[0].
The capture feature is associated with one of the boards timers. In our case, this is timer 1 (hence CAP1). The [0] indicates port 0. Timer 1 is used by the development package, so it should already be started for us. All we need to do is configure the Capture Control Register to generate an input on both rising and falling edges.
When this interrupt occurs, we get a tick count. We don't need to know the time exactly, just the difference in times between captures. Since the IR receiver idles high, we know the first interrupt will be for a falling edge. We can perform the calculations according to this assumption.
Software Design
IR Receive Task
There are two components that make the IR receive functionality work. The first is the task itself, which processes commands from the Bluetooth task and saves IR timings to the SD card.
The receive task and capture code was modified from Preet's IR_Sensor and remoteTask classes. This was a great starting place but needed to be modified to suit the project needs.
This task sleeps indefinitely until it receives a message on the mRXQueue. If this message contains a learn command, it will attempt to take the mutex shared by the transmit task. If it can take it, that means the system is not transmitting and it's safe to start recording timing signals. If it can't take it, then the task will let the Bluetooth task know the system is busy and cannot learn new commands.
However, if learning can continue, we signal the second part of the IR receive functionality. We tell the IR_Sensor singleton class to clear any stored captures and wait until a new command is received or a timeout occurs. Next, we retrieve the array of timings and store it on the SD card for the transmit task to use.
The code segment and flow chart to show this behavior are shown below.
bool remoteRXTask::run(void *p)
{
u0_dbg_printf("Learning...\n" );
//Receive item from queue
xQueueReceive(rxQueue);
//Check if it is a learn command and semaphore avaliable
if ((learn_command) && (xSemaphoreAvaliable))
{
//Read IR code
IS.clearCode();
while(!IS.isIRCodeReceived())
{
vTaskDelayMs(100);
}
uint8_t max_timings = 255;
uint16_t timings[max_timings] = {0};
uint16_t count;
IS.getLastIRCode(timings, &count, max_timings);
//get+store the code and send feedback here
sprintf(filename,"1:%c.hex",mMsg.button);
Storage :: write(filename,(void *)&timings,count,0);
xSemaphoreGive(mIRMutex);
}
else
{
strcpy(mMsg.msg,"*****Busy Transmitting***");
//Send item to the Bluetooth queue
xQueueSend(mBtQueue,&mMsg,0);
}
return true;
}
The code below is the core of what makes the IR learning possible. If a rising or falling edge occurs on the capture pin, then an interrupt is generated. In this case, storeIrCode() will be called which saves the timestamps to an array. We set a timeout of 50ms, after which decodeIrCode() is called that calculates the differences in timestamps. These differences are the timings we will store and later transmit.
The 50ms timeout was chosen because some remotes have more complicated commands that include two consecutive commands. The time between those commands was measured to be about 40ms with the tested remotes.
#if (1 == SYS_CFG_SYS_TIMER)
/* ISR for captured time of the capture input pin */
if (intr_reason & timer_capt0_intr_ir_sensor_edge_time_captured)
{
gp_timer_ptr->IR = timer_capt0_intr_ir_sensor_edge_time_captured;
// Store the IR capture time and setup timeout of the IR signal (unless we reset it again)
IS.storeIrCode(gp_timer_ptr->CR0);
//10k was too short of a timeout for long commands,
//which sometimes have separations of up to 40ms
gp_timer_ptr->MR2 = 50000 + gp_timer_ptr->TC;
}
/* MR2: End of IR capture (no IR capture after initial IR signal) */
else if (intr_reason & timer_mr2_intr_ir_sensor_timeout)
{
gp_timer_ptr->IR = timer_mr2_intr_ir_sensor_timeout;
IS.decodeIrCode();
}
/* MR0 is used for the timer rollover count */
else
#endif
void IR_Sensor::storeIrCode(uint32_t value)
{
if (!m_ready_to_receive)
return;
// Just store the timestamp of this signal
if(g_signal_count < MAX_EDGES_PER_IR_FRAME)
{
g_ir_edge_timings[g_signal_count++] = value;
}
}
void IR_Sensor::decodeIrCode(void)
{
if (!m_ready_to_receive)
return;
for (int i = 0; i < g_signal_count-1; i++)
{
g_ir_cmd_timings[i] = g_ir_edge_timings[i+1]-g_ir_edge_timings[i];
}
m_code_received = true;
m_ready_to_receive = false;
}
IR Transmit Task:
The IR transmit task will be signaled by the Bluetooth task. After being signal the task will attempt to read from a file using the Storage class. If the file doesn't exist it will send an error message the terminal. If the file does exist then the task will read all the timing values into an array. The task will then enter a PortCritical area and transmit the IR signal by turning on and off the PWM pin. Once it is finished the IR Transmit task return the Mutex.
remoteTXTask(uint8_t priority) :scheduler_task("rTX", 2048, priority), pwm0(38Khz)
bool run(void *p)
{
xQueueReceive;
if ((command == send_cmd) && xSemaphoreTakeAvailable)
{
u0_dbg_printf("Transmitting...\n" );
int size = readFile(mMsg.fileName);
portENTER_CRITICAL();
for(int i = 0; i < size; i++)
{
if(i % 2 == 0) pwm1.set(25);
else pwm1.set(0);
delay_us(timing_values[i]);
}
pwm1.set(0);
portEXIT_CRITICAL();
}
xSemaphoreGive(mIRMutex);
return true;
}
Android Application with SJOne Board Software Design
The Android application Bluetooth task is to provide the a communication link between all the tasks. The Bluetooth task would receive commands from the application, then send items to the queue with the appropriate information such as command, button number, and message from the Android application.
Pseudo Code:
void BluetoothTask(void *p) {
//Initialize transmit, receive and bluetooth queues
bool init();
bool run()
{
//Get command from Android app
uart3.gets();
//Check if it is a transmit or learn command
checkCommand();
//Send item to the appropriate queue
xQueueSend();
//Sleep to reduce CPU usage
vTaskDelay(10);
}
}
Remote Learner Android App
The software development environment for the Remote Learner Android application is Android Studio 1.2.1.1, it is the official IDE for Android application development, based on IntelliJ IDEA. The IDE simplified the way how Android application was developed traditionally. It comes with a built-in Android Emulator so there was no need additional plug-in or downloads. However, in the Remote Learner application, Android emulator was not very helpful because it requires an active Bluetooth connection. Hence, a real Android device was used throughout development. Figures below show the Remote Leaner Android application and its design layout. At the same time, it has the capability to receive status from the SJOne board after an command has been sent.
The Remote Learner application initial screen is shown in the left figure below. It has the capability to pair up with an Bluetooth device, then transmit commands to the device. Under the application title, there is a status message that updates instantaneously from initiating and establishing Bluetooth connection requests.
Once a Bluetooth connection has been established. The application would allow user to send button commands to the Bluetooth device. For example, to learn a button, the user would need to hold the button on the application, and wait for a reply message from the SJOne board Bluetooth task. Once a button is learned successfully, it would display a status message as shown in the middle figure below. If the request to learn a button failed, it would display a status message as shown in the right figure below.
Testing & Technical Challenges
IR Transmit Issue #1
PROBLEM:
IR transmission signal was not being received by IR receiver.
RESOLUTION:
Used Oscilloscope to discover that PWM instance set to 38,000 Hz did not produce a 38 kHz. Had to adjust the value to about 3205 Hz to get a PWM signal that was about 38Khz.
FUTURE RECOMMENDATIONS:
Examine PWM code and determine if there is a way to enter the fequency in Khz.
IR Transmit Issue #2
PROBLEM:
Putting IR transmission signal in task was causing IR signal not to be received because task was being interrupted.
RESOLUTION:
Placed time sensitive code in a Port Critical section to prevent that section of code being interrupted.
IR Transmit Testing
To test the IR transmitter we first used an oscilloscope to capture a IR signal from a remote control. After capturing the signal we determined the protocol. To replicate the signal we created an array with the timing values. The array values that were even index values represented the timing for the IR led to toggle on. The odd values represent the timing values that the IR LED need to be off. Using a for loop, we send the signal and tested using an oscilloscope first. After determining that the signal was similar to the capture, we tested with a few devices. The first device was an Apple remote to turn the volume up. Afterward we tested a different device using a different protocol. We were able to figure channel up/down and volume up/down and control a Sharp TV. The final test for integration was to control an LG TV. See demo video of the device controlling LG TV.
Android App Testing
The initial testing and debugging was used with the Android emulator. The built-in emulator allows real time debugging, it was very helpful for confirming simple events within the application. After Bluetooth chat service was implemented, a real Android device and logging was used for testing and debugging.
Conclusion
Our team was successful in completing the Remote Learner project. We faced a few issues such as debugging the PWM frequency to the correct value and integrating the project together. Our team expanded our knowledge of using FreeRTOS to implement individual task and having them communicate with each other thru queue. We learned about the different protocols such as NEC that was used by some of the devices we tested. Lastly, we learn how to develop an Android application to control everything.
Project Video
https://www.youtube.com/watch?v=9CvH5DOZiIU
Project Source Code
References
Acknowledgement
Any acknowledgement that you may wish to provide can be included here.
References Used
List any references used in project.
Appendix
You can list the references you used.