// RC5 Protocol decoder Arduino code
// Define number of Timer1 ticks (with a prescaler of 1/8)
#define short_time 1400 // Used as a minimum time for short pulse or short space ( ==> 700 us)
#define med_time 2400 // Used as a maximum time for short pulse or short space ( ==> 1200 us)
#define long_time 4000 // Used as a maximum time for long pulse or long space ( ==> 2000 us)
#define STATE_ADDRESS 0 // Address in EEPROM where we store the selected channel
#define CMD_ARRAY_SIZE 11
#define UP true
#define DOWN false
#define MAX_CHANNEL 5
#define POWER_PIN A5
#define MUTE_PIN A4
#define PWR_BTN_PIN A0
#define MUTE_BTN_PIN A1
#define UP_BTN_PIN A2
#define DN_BTN_PIN A3
// include LCD library code
#include <LiquidCrystal.h>
#include <EEPROM.h>
// LCD module connection (RS, E, D4, D5, D6, D7)
LiquidCrystal lcd(3, 4, 5, 6, 7, 8);
char text[3];
boolean rc5_ok = 0, toggle_bit;
boolean mute = false;
boolean power = false;
boolean eeprom_changed = false;
byte rc5_state = 0, j, address, command, board_state;
unsigned int rc5_code;
int channel = 0;
int command_id, last_command_id, last_exec_cmd_id;
unsigned long lastDebounceTime = 0; // the last time the output pin was toggled
unsigned long debounceDelay = 50; // the debounce time; increase if the output flickers
byte channelPins[] = {9, 10, 11, 12, 13};
byte motorPins[] = {0, 1};
typedef struct rc5_key
{
byte address;
byte key;
char name[16];
};
rc5_key commands[CMD_ARRAY_SIZE] = {
{17, 1, "CD. "},
{17, 2, "PC. "},
{17, 3, "Tape 1"},
{17, 4, "Tape 2"},
{17, 5, "Tuner "},
// {17, 6, "AUX"},
{17, 32, "Chan_Up "},
{17, 33, "Chan_Dn "},
{16, 16, "Vol_Up "},
{16, 17, "Vol_Down"},
{16, 12, "Power "},
{16, 13, "Mute "}
};
int getCommandId(byte rc5_address, byte rc5_key) {
for (byte i=0; i< CMD_ARRAY_SIZE; i++) {
if (commands[i].address == rc5_address &&
commands[i].key == rc5_key) {
return i;
}
};
}
byte getBoardState() {
byte state=0;
state += channel;
state += (power << 3);
state += (mute << 4);
return state;
}
byte getChannelFromState(byte state) {
return (state & 7);
}
boolean getMuteFromState(byte state) {
return (state & 16) >> 4;
}
boolean getPowerFromState(byte state) {
return (state & 8) >> 3;
}
void setBoardState (byte state, boolean includePower) {
if (state > 0 && state < 32) {
channel = getChannelFromState(state);
power = getPowerFromState(state);
mute = getMuteFromState(state);
} else {
channel = 1;
power = 1;
mute = 0;
sprintf(text, "%d%s", state, " ");
lcd.setCursor(9, 1);
lcd.print(text); // Display command in hex format
}
setChannel(channel);
if (includePower) setPower(power);
setMute(mute);
}
void executeCommand(byte command_id) {
if (command_id>=0 && command_id < CMD_ARRAY_SIZE) {
if (command_id < MAX_CHANNEL) { // this means a number from 1 to MAX_CHANNEL was pushed
setChannel(command_id+1);
} else {
switch (command_id) {
case MAX_CHANNEL:
// Channel_Up
channel++;
if (channel > MAX_CHANNEL) {
channel = MAX_CHANNEL;
}
setChannel(channel);
break;
case MAX_CHANNEL + 1:
// Channel_Down
channel--;
if (channel < 1) {
channel = 1;
}
setChannel(channel);
break;
case MAX_CHANNEL + 2:
// Volume_Up
VolumeUpDn(UP);
break;
case MAX_CHANNEL + 3:
// Volume_Down
VolumeUpDn(DOWN);
break;
case MAX_CHANNEL + 4:
// Toggle Power
togglePower();
break;
case MAX_CHANNEL + 5:
// Toggle Mute
toggleMute();
break;
default:
// Nothing
break;
}
}
}
}
void VolumeUpDn(boolean direction) {
// direction: up=true, down=false
digitalWrite(motorPins[0], direction);
digitalWrite(motorPins[1], !direction);
delay(200);
digitalWrite(motorPins[0], HIGH);
digitalWrite(motorPins[1], HIGH);
}
void setChannel(int channel_id) {
for(byte index = 0; index < MAX_CHANNEL; index++) {
digitalWrite(channelPins[index], LOW);
}
digitalWrite(channelPins[channel_id-1], HIGH);
lcd.setCursor(9, 1);
sprintf(text, "%s", commands[channel_id-1].name);
lcd.print(text);
}
void togglePower(){
power = !power;
setPower(power);
}
void toggleMute(){
mute = !mute;
digitalWrite(MUTE_PIN, mute);
}
void setPower(boolean isOn){
byte read_state;
power = isOn;
if (!power) {
for(byte index = 0; index < MAX_CHANNEL; index++) {
digitalWrite(channelPins[index], LOW);
} // turn channels off
digitalWrite(MUTE_PIN, LOW); // turn off mute led
EEPROM.write(STATE_ADDRESS, board_state); // save state to EEPROM
} else {
read_state = EEPROM.read(STATE_ADDRESS);
// setChannel(getChannelFromState(board_state));
// setMute(getMuteFromState(board_state));
setBoardState(read_state, false);
}
digitalWrite(POWER_PIN, isOn);
}
void setMute(boolean isMute){
mute = isMute;
digitalWrite(MUTE_PIN, isMute);
sprintf(text, "%d%s", mute, " ");
lcd.setCursor(9, 1);
lcd.print(text); // Display command in hex format
}
void setup() {
EEPROM.write(STATE_ADDRESS, 255);
for(byte index = 0; index < MAX_CHANNEL; index++) {
pinMode(channelPins[index],OUTPUT);
}
for(byte index = 0; index < 2; index++) {
pinMode(motorPins[index],OUTPUT);
digitalWrite(motorPins[index], HIGH);
}
pinMode(POWER_PIN,OUTPUT);
pinMode(MUTE_PIN,OUTPUT);
pinMode(PWR_BTN_PIN,INPUT);
pinMode(MUTE_BTN_PIN,INPUT);
pinMode(UP_BTN_PIN,INPUT);
pinMode(DN_BTN_PIN,INPUT);
// set up the LCD's number of columns and rows
lcd.begin(16, 2);
lcd.setCursor(0, 0);
lcd.print("ADS:0x00 TGL: 0");
lcd.setCursor(0, 1);
lcd.print("CMD:0x00");
// Timer1 module configuration
TCCR1A = 0;
TCCR1B = 0; // Disable Timer1 module
TCNT1 = 0; // Set Timer1 preload value to 0 (reset)
TIMSK1 = 1; // enable Timer1 overflow interrupt
attachInterrupt(0, RC5_read, CHANGE); // Enable external interrupt (INT0)
board_state = EEPROM.read(STATE_ADDRESS);
setBoardState(board_state, true);
// setChannel(channel);
}
void RC5_read() {
unsigned int timer_value;
if(rc5_state != 0){
timer_value = TCNT1; // Store Timer1 value
TCNT1 = 0; // Reset Timer1
}
switch(rc5_state){
case 0 : // Start receiving IR data (initially we're at the beginning of mid1)
TCNT1 = 0; // Reset Timer1
TCCR1B = 2; // Enable Timer1 module with 1/8 prescaler ( 2 ticks every 1 us)
rc5_state = 1; // Next state: end of mid1
j = 0;
return;
case 1 : // End of mid1 ==> check if we're at the beginning of start1 or mid0
if((timer_value > long_time) || (timer_value < short_time)){ // Invalid interval ==> stop decoding and reset
rc5_state = 0; // Reset decoding process
TCCR1B = 0; // Disable Timer1 module
return;
}
bitSet(rc5_code, 13 - j);
j++;
if(j > 13){ // If all bits are received
rc5_ok = 1; // Decoding process is OK
detachInterrupt(0); // Disable external interrupt (INT0)
return;
}
if(timer_value > med_time){ // We're at the beginning of mid0
rc5_state = 2; // Next state: end of mid0
if(j == 13){ // If we're at the LSB bit
rc5_ok = 1; // Decoding process is OK
bitClear(rc5_code, 0); // Clear the LSB bit
detachInterrupt(0); // Disable external interrupt (INT0)
return;
}
}
else // We're at the beginning of start1
rc5_state = 3; // Next state: end of start1
return;
case 2 : // End of mid0 ==> check if we're at the beginning of start0 or mid1
if((timer_value > long_time) || (timer_value < short_time)){
rc5_state = 0; // Reset decoding process
TCCR1B = 0; // Disable Timer1 module
return;
}
bitClear(rc5_code, 13 - j);
j++;
if(timer_value > med_time) // We're at the beginning of mid1
rc5_state = 1; // Next state: end of mid1
else // We're at the beginning of start0
rc5_state = 4; // Next state: end of start0
return;
case 3 : // End of start1 ==> check if we're at the beginning of mid1
if((timer_value > med_time) || (timer_value < short_time)){ // Time interval invalid ==> stop decoding
TCCR1B = 0; // Disable Timer1 module
rc5_state = 0; // Reset decoding process
return;
}
else // We're at the beginning of mid1
rc5_state = 1; // Next state: end of mid1
return;
case 4 : // End of start0 ==> check if we're at the beginning of mid0
if((timer_value > med_time) || (timer_value < short_time)){ // Time interval invalid ==> stop decoding
TCCR1B = 0; // Disable Timer1 module
rc5_state = 0; // Reset decoding process
return;
}
else // We're at the beginning of mid0
rc5_state = 2; // Next state: end of mid0
if(j == 13){ // If we're at the LSB bit
rc5_ok = 1; // Decoding process is OK
bitClear(rc5_code, 0); // Clear the LSB bit
detachInterrupt(0); // Disable external interrupt (INT0)
}
}
}
ISR(TIMER1_OVF_vect) { // Timer1 interrupt service routine (ISR)
rc5_state = 0; // Reset decoding process
TCCR1B = 0; // Disable Timer1 module
}
void loop() {
command_id = -1;
if ((digitalRead(PWR_BTN_PIN) ==0) && command_id == -1) {
command_id = MAX_CHANNEL + 4; //Power
}
if ((digitalRead(MUTE_BTN_PIN) ==0) && command_id == -1) {
command_id = MAX_CHANNEL + 5; //Mute
}
if ((digitalRead(UP_BTN_PIN) ==0) && command_id == -1) {
command_id = MAX_CHANNEL; // Channel_Up
}
if ((digitalRead(DN_BTN_PIN) ==0) && command_id == -1) {
command_id = MAX_CHANNEL + 1; // Channel_Dn
}
if((rc5_ok) ) { // If the mcu receives RC5 message with successful
rc5_ok = 0; // Reset decoding process
rc5_state = 0;
TCCR1B = 0; // Disable Timer1 module
toggle_bit = bitRead(rc5_code, 11); // Toggle bit is bit number 11
address = (rc5_code >> 6) & 0x1F; // Next 5 bits are for address
command = rc5_code & 0x3F; // The 6 LSBits are command bits
command_id = getCommandId(address, command);
executeCommand(command_id);
lcd.setCursor(15, 0);
lcd.print(toggle_bit); // Display toggle bit
sprintf(text, "%02X", address);
lcd.setCursor(6, 0);
lcd.print(text); // Display address in hex format
sprintf(text, "%02X", command);
lcd.setCursor(6, 1);
lcd.print(text); // Display command in hex format
attachInterrupt(0, RC5_read, CHANGE); // Enable external interrupt (INT0)
}
if (command_id >= 0 && command_id != last_command_id) {
lastDebounceTime = millis();
last_exec_cmd_id = -1;
}
if ( ((millis() - lastDebounceTime) > debounceDelay) ) {
if (last_exec_cmd_id != command_id
&& command_id >= 0
&& (power || (command_id==MAX_CHANNEL + 4))
) {
executeCommand(command_id); // call to procedure where we take action on remote commands
board_state = getBoardState();
last_exec_cmd_id = command_id;
}
}
last_command_id = command_id;
}