I'm new to Arduino but I have been doing a great deal of research and have a project mostly thought out (and partially programmed) but was needing some expert advice on the best way to finalize the programming.
Project
Project is based on adding a "volume knob" to my Clarion Car Stereo; which only has "push buttons" currently. I have a sketch and breadboard wired up that reads the rotary encoder (new volume knob) and understands the clockwise/counterclockwise turns and displays each click as a positive increment or negative increment on an LCD screen.
Got this information from : FarrellF.com
Goal/Help Needed
My goal (and what I need help with) is adding code to the sketch that will allow an IR emitter to transmit the appropriate "remote code" to the IR receiver on the car stereo. I have the Clarion remote codes, so basically, I need help with how to tell the IR to send a "Volume Up" command if the encoder is turned clockwise. Likewise, I would need the IR to transmit a "volume down" command if the encoder is turned counterclockwise.
I plan on keeping the small LCD to have a graphical display of the encoder reading, but my car stereo also has a 7: LCD that will show the volume increasing/decreasing if everything is transmitted correctly.
Options?
Originally, I bounced a few ideas around on how to send the commands to the stereo. I could tap into the existing volume buttons (which is still an option), tap into the Steering Wheel Control inputs (don't know the protocol Clarion uses for this input), or use the IR receiver that was originally used for the wireless remote ( which died about 6 months after purchasing the unit). Since I know the remote codes and I can easily add additional functions with IR commands, this seemed the easiest and most expansive option for the short and long term.
So, can anyone assist me in what I need to add to the sketch to allow for the IR to transmit the appropriate remote codes based on the encoder's output? Again, I used the diagram and sketch from FarrellF.com This is where I am at this point.
"You could get even more functionality by adding an on and off signal based on the speed of turning the rotary."
And you could change how much you add/subtract from the volume by looking at the rate of change of the encoder. If you see that the pulses are coming in faster than what would be normal, you could multiply the difference by two for instance.
rootboy - thanks for posting the revised code. I will look at add to my sketch and try to add the Clarion remote codes to it and see how it functions.
In your latest post - how would you recommend adding the speed factor to the volume control. do you think it would work well considering I'm just sending a vol + command via IR verses connecting directly to the volume buttons?
OK - so I added some commands/code to reflect the Clarion IR codes. Does this look right to everyone? I got some of it from the IRremote Lib site and some from a similar sketch that has other functions unrelated to this project
#include <LiquidCrystal.h>
#include <IRremote.h>
IRsend irsend;
LiquidCrystal lcd(10, 9, 5, 4, 3, 2); // RS, Enable, D4, D5, D6, D7
int count = 0;
int precount = 0; // <--Add this
int diffcount = 0; // <--Add this
byte currentA, currentB, previousA, previousB = 0;
int IRledPin=6; //IR Led pin PWM 6
void setup() {
lcd.begin(16, 2);
previousA = currentA = digitalRead(12);
previousB = currentB = digitalRead(11);
Serial.begin(9600);
}
void loop() {
previousA = currentA;
previousB = currentB;
currentA = digitalRead(12);
currentB = digitalRead(11);
if(previousA == LOW && previousB == LOW && currentA == HIGH && currentB == LOW) // clockwise
count++;
else if(previousA == HIGH && previousB == HIGH && currentA == LOW && currentB == HIGH) // clockwise
count++;
else if(previousA == LOW && previousB == LOW && currentA == LOW && currentB == HIGH) // counter-clockwise
count--;
else if(previousA == HIGH && previousB == HIGH && currentA == HIGH && currentB == LOW) // counter-clockwise
count--;
lcd.setCursor(0, 0);
lcd.print(count);
lcd.print(" ");
if (count != precount) { // <--Add this
setting = setting + count - precount; // <--Add this
if (setting > 11) setting = 11; // Everyone knows you can only crank it to 11... (Spinal Tap) <-- Add this
if (setting < 0) setting = 0; // // <--Add this
precount = count; // <--Add this
if ( setting.read() != 11
{
for (int i = 0; i < 3; i++) {
irsend.sendNEC (0x6106D827, 32); // Clarion Volume + Code
delay(100);
}
if ( setting.read() != 0
{
for (int i = 0; i < 3; i++) {
irsend.sendNEC (0x6106B847, 32); // Clarion Volume - Code
delay(100);
}
} // <--Add this
}
}
arshull:
rootboy - thanks for posting the revised code. I will look at add to my sketch and try to add the Clarion remote codes to it and see how it functions.
In your latest post - how would you recommend adding the speed factor to the volume control. do you think it would work well considering I'm just sending a vol + command via IR verses connecting directly to the volume buttons?
First off, sorry about the late reply, we have been having equipment problems here at the plant and I have been busy.
As for your rate detection, what I would try is to have a variable count up each time the encoder sends out a pulse. Create yourself a 100 mS interval timer which fires off an interrupt which reads the current value of the variable, and then resets it. Looking at the magnitude of the variable you will be able to tell if you are moving it fast, slow, or not at all.
Another way would be to create three signed variables, one called "currentCount", another called "previousCount", and the third called "changeRate".
Load previousCount with the encoder's position.
Using the same timer interrupt, when it fires subtract previousCount from currentCount and store the result in changeRate.
Then load the value of currentCount into previousCount.
Be sure to do a limit test so that you don't overflow any of your variables. The simplest way would be to let your previousCount variable get to around the midpoint of its range (about 16k). Do steps 1 & 2, but for 3 zero out previousCount and load changeRate into currentCount.
This way changeRate will not only give you the same information as the first example, but it will indicate direction as well.
I've got some encoders in my laptop bag, when (if) I get some time I will work a circuit up and see if I can make a routine for you.
Thanks for the response. I have not worked on this in a while, but now picking it up again. I need to get this project done so I can move on to some other things.
This is the code as I have it now. It compiles and functions (to an extent). When I turn the encoder, the volume goes up, no matter which direction I turn it. At least it's transmitting the command, but obviously my code is still not quite right.
In reading through the suggestions above, most of it is still greek to me. I just don't know enough about the specific language and/or the various libraries/function they provide. I did update my IR library per the link you provided.....so hopefully that helps in the end.
Regardless, please review my latest code and let me know what I need to change in order to get this working properly. Any specific code is greatly appreciated b/c I just don't know how to transfer "suggestions" into real code yet.
PaulS:
Why are you sending data on every pass through loop()? I'd expect you to send data only when count changed.
I'd also expect you to be using interrupts to detect the encoder changes. Though polling may be fast enough.
I also don't see any point in any of the delay()s.
Haha - I have no idea. Like I mentioned before, I am completely new to this and have no experience developing sketches or development of any kind. This is why I have asked for assistance.... hopefully I will learn from correcting the code and grasp a better understanding of the various dependencies that are required in the sketch's.
How would you suggest that I resolve the problems you mentioned before? Again, specifics please b/c I am not able to translate suggestions into revised code yet.
As an update.... the suggestion by Jiggy-Ninja worked! Now my encoder controls the vol + and Vol - of my Clarion head unit via IR transmissions. Thanks again to Jiggy-Ninja and rootboy for their help on this project!
For future reference, here is the full sketch in case anyone is working on a similar project down the road:
The only improvement I would like to make to this is for the volume to increase based on the speed the encoder is turned. currently, it increases/decreases as fast as the ir can transmit the turn pulses but it would be nice for a speed augmentation to be implemented when the encoder is turned quickly to suddenly drop or raise the volume quickly.
It would be possible to use this code without the LCD screen? only the encoder sending signal to the IR emitter ?
To increase / decrease the volume and when pressed trigger the mute ...
There is!! I got it!!
I made some adjustments in the code to later add 3 more switches (Source; Mute; NextStation). When I press the Encoder the Mute button is activated.
P.S. Replace the code for the desired device.
I will replace the UNO by a Nano Arduino to reduce the circuit size...
Next step, create a rotary volume control; play / pause; stop; next; foward; to my pc with an Arduino micro pro ...
Thank you so much, for the post !!
these are the changes that I made...
#include <IRremote.h>
//========Buttons ins config
const int mute = 10;//the Encoder SW
const int source = 9;
const int next = 8;
int buttonState = 0;
//==========Rotary Encoder
int count = 0;
int precount = 0;
int diffcount = 0;
byte currentA, currentB, previousA, previousB = 0;
IRsend irsend; //=====conect the IR_Led to port ~3 pwm
void setup() {
Serial.begin(9600);//====start serial (optional)
previousA = currentA = digitalRead(11);
previousB = currentB = digitalRead(12);
//=====Buttons Config
pinMode(mute, INPUT);
pinMode(source, INPUT);
pinMode(next, INPUT);
}
void loop() {
//==========================ENCODER================================
previousA = currentA;
previousB = currentB;
currentA = digitalRead(11);
currentB = digitalRead(12);
if(previousA == LOW && previousB == LOW && currentA == HIGH && currentB == LOW) // clockwise
{
count++;
irsend.sendNEC(0x6106D827, 32); // Volume UP Code
Serial.println(count);
}
if(previousA == HIGH && previousB == HIGH && currentA == LOW && currentB == HIGH) // clockwise
{
count++;
irsend.sendNEC(0x6106D827, 32); // Volume UP Code
Serial.println(count);
}
if(previousA == LOW && previousB == LOW && currentA == LOW && currentB == HIGH) // counter-clockwise
{
count--;
irsend.sendNEC(0x6106B847, 32); // Volume DOWN Code
Serial.println(count);
}
if(previousA == HIGH && previousB == HIGH && currentA == HIGH && currentB == LOW) // counter-clockwise
{
count--;
irsend.sendNEC(0x6106B847, 32); // Volume DOWN Code
Serial.println(count);
}
//===================================BUTTONS========================
//MUTE
if (digitalRead(mute) == HIGH) {
irsend.sendNEC(0x39C600FF, 32);// MUTE Code
}
//SOURCE
if (digitalRead(source) == HIGH) {
irsend.sendNEC(0x39C6807F, 32);// SOURCE Code
}
//NEXT
if (digitalRead(next) == HIGH) {
irsend.sendNEC(0x39C6E817, 32); // NEXTStation Code
}
}