Let’s hit this nail on the head. Last time I talked about some general considerations regarding the code. Now I want to go through the code in blocks and, hopefully, give you an understanding of what I did and why I did it for you to know what works or, at least, what to steer clear off.
Now for the actual code I came up with: This is version 0.2 that I will be testing and probably improving upon if needed. I am sure this can be optimized but for now I want to seal the control module and get it tested in a real environment (yes, I have already built the module, I will cover that later). So here are the main code blocks:
Include directives
The only thing I need is to include IRLib2 by cyborg5. I’ll do a separate posting about IRLib2. All you need to know for now is that:
- It’s a library that permits reading and transmitting of IR signals from remotes etc.
- It does all the magic at the end of the program in one call
- It’s a great library that I fully encourage you to experiment with
First the library itself is included then the protocols and finally combo (IRLibCombo.h) that ties all of it together. Basically, this part is just using what I need from cyborg5’s great documentation here: https://github.com/cyborg5/IRLib2/blob/master/IRLib2/examples/send/send.ino .
//IR LED #include "IRLibAll.h" //https://github.com/cyborg5/IRLib2 //Include the send base #include <IRLibSendBase.h> //The lowest numbered protocol should be first but remainder can be any order. #include <IRLib_P02_Sony.h> #include <IRLibCombo.h> //Create a sender Object (always pin 3 ) IRsend mySender;
Global variables
The global variables I am using are grouped by usage:
- ohmmeter: the pin I am using to detect the voltage, a variable to store the value red by the DAC (raw), Vin to hold the computed voltage and Vref to hold the reference voltage which I measured on my Arduino being ran by the USB input (4.95V).
- light controls: just the pin to control the lights on the factory buttons. I chose a PWM capable pin because I wanted to be able to flash and pulse the lights to show feedback but testing showed that they are not bright enough to see in the daytime so relying on any feedback from them would be useless. Come to think of it I could still have implemented it just not rely on it … mental note: further improvement opportunity.
- Sony controls: these are just two Booleans to store the state of the buttons. Shift means that a long press was detected and super shift means that I pressed the button that activates the door controls.
- door controls: just declarations for the pins used for locking and unlocking and a delay of 100 that represents the time I want the lock/unlock signal held high
- debounce: 4 values used for preventing multiple accidental activations on one button push.
- debug: I like switching off the debug text to free up processing power once the module is installed. Sending by serial takes time even with the high baud rates Arduino can handle
//ohmmeter int ohmeterPin= A0;//pin 0 analog int raw= 0;//initialize DAC output float Vin= 5.0;//initialize sensed voltage float Vref= 4.95;//ref voltage //light control byte lightPower=11;//the pin that constols the steering weel leds - pwm in case of brighter leds //sony controls bool shift=false;//modifier for long press bool superShift=false;//modifier for door control safety //door controls byte doorLock=13;//pin for door lock signal byte doorUnlock=12;//pin for door unlock safety int doorDelay=100;//door control signal time //debounce long int duration=0;//duration of button press int inputThreshold=700;//threshold for false input signals int debounceThreshold=25;//threshold for debounce time int shiftThreshold=500;//threshold for activating shift modifier //debug bool debug=false;//everything works faster when tehre's not alot of waiting to comunicate going on
Setup
In the setup I just initialized serial communications with the PC for debugging set the needed output and input pins and printed a message to confirm in the Serial Monitor that everything is ready
void setup() { Serial.begin(115200); pinMode(doorLock, OUTPUT); pinMode(doorUnlock, OUTPUT); pinMode(lightPower, OUTPUT); pinMode(ohmeterPin, INPUT); Serial.println("INITIALISED"); }
Loop
I turned the button backlighting on to full power and call SENSE. Remember I am controlling the backlighting through a PWM pin and 255 is the maximum value. Although I could have set this in the setup I just wanted to make sure I never forget the backlight off.
void loop(){ analogWrite(lightPower, 255); // power on the leds sense();//start infinetly sensing }
Sense
The things I want done in the sense function are:
- Make sure the voltage read from the resistive divider is within limits
- Once an accurate reading is received readings are taken periodically to determine the length of time a button was pressed and the average value of the received input values from the ADC is calculated. This value is used as a way of eliminating errors caused by imperfect contacts etc.
- If the above time measurement exceeds the threshold for a so called long press the measuring routine is exited
- Once it is determined I have a valid button press with a corresponding duration the average input voltage is calculated based on the raw value provided by the ADC and the DETECT function is called. In the case of a long press it is presumed the DETECT function will be called while an input is still pressed so after Detect the necessary action the code waits for the button to be depressed.
void sense(){ bool check = false; raw = analogRead(ohmeterPin);///read the value if(debug){Serial.println(raw);} if(raw<inputThreshold)//if the value is less than threshold discard { long start=millis();//note when the fisrt buton activation occured long sum=raw;//first raw value is already loaded when we enter the while loop int i=1;//first raw value is already loaded when we enter the while loop analogWrite(lightPower, 0);//give the user some feedback that the button is pressed while(raw < 700 )//as long as the ADC value is smaller than the threshold keep reeding and checking { raw=analogRead(ohmeterPin); duration=millis()-start; sum=sum+raw; i++; if(debug){ Serial.print("raw_inproc = "); Serial.println(raw); Serial.print("i_inproc = "); Serial.println(i); Serial.print("sum_inproc = "); Serial.println(sum); } if(duration>shiftThreshold)break; } raw=sum/i; //raw becomes avrage if(debug) { Serial.print("i_final = "); Serial.println(i); Serial.print("sum_final = "); Serial.println(sum); Serial.print("raw_final : "); Serial.println(raw); } analogWrite(lightPower, 255);//once the buton is depressed turn on the lights check=true; } if(check) { Vin= raw * Vref /1024.0;//calculate Vin if(debug) { Serial.print("time pressed : "); Serial.println(duration); } if(shiftThreshold>(duration)&&(duration)>debounceThreshold) { //if the buton was pressed less that the shift threshold and more than the debounce time make shure shift is false and start executing shift=false; Serial.println("Short press detection :"); detect(Vin); } if((duration)>shiftThreshold) { //if the button has been pressed long enaugh trigger the modifier and start executing shift=true; Serial.println("Long press detection :"); detect(Vin); raw=analogRead(ohmeterPin); if(debug) { Serial.print("release check: "); Serial.println(raw); } while(raw<inputThreshold)//wait for button to be released as to not trigger 1 long and 1 short press { if(debug) { Serial.print("waiting for release : "); Serial.println(raw); } raw=analogRead(ohmeterPin); } if(debug){Serial.println("released");} } duration=0;//reset the start moment just to be safe } }
Detect
This function simply calls the execute function with a parameter representing the necessary code to be sent. This parameter is determined by a series of cascading IF statements that take into account the length of a button press (the shift Boolean is set to true for long presses) and also if the “SuperShift” was activated which is also stored in a Boolean.
void detect(float Voltage){ //checks the voltages and executes the propper comands to the leds of to variabels //resistor values : 82 82 120 180 330 680 2200 //totals : 82 164 284 464 794 1474 3674 //voltages :0.379 0.704 1.106 1.585 2.213 2.979 3.93 //thresholds : 0.5415 0.905 1.335 1.899 2.596 3.184 Serial.print("Vin: "); Serial.println(Voltage); if(Voltage>3.184) { Serial.println("Out of range");//should hardly happen }else if(Voltage>2.596) { //| //SuperShift/5 Serial.println("|"); if(!shift){ superShift=!superShift; Serial.print("superShift :"); Serial.println(superShift); }// short command enable door comands else { execute(0x1021);//long command 5key superShift=false; } shift=false; }else if(Voltage>1.899 ) { //< //seek- 2(alb+) Serial.println("<"); if(!shift)execute(0x5621);//short command seek- else execute(0x4021);//long command 2key shift=false; superShift=false; }else if(Voltage>1.335) { //> //seek+ 1(alb-) Serial.println(">"); if(!shift)execute(0x1621);//short command seek+ else execute(0x21);//long command 1key shift=false; superShift=false; }else if(Voltage>0.905) { //O //source 6(pause) Serial.println("O"); if(!shift)execute(0x3121);//short command source else execute(0x5021);//long command 6key shift=false; superShift=false; }else if(Voltage>0.5415 ) { //+ //vol+ 3(rep) SS:LOCK Serial.println("+"); if(superShift && shift){lock();}//if it's a long press and the door controls where enabeld just before lock else { if(!shift)execute(0x2421);//short command vol+ else execute(0x2021);//long command 3key } shift=false; superShift=false; }else { //- //vol- 4(shuf) SS:UNLOCK Serial.println("-"); if(superShift && shift){unlock();}//if it's a long press and the door controls where enabeld just before unlock else { if(!shift)execute(0x6421);//short command vol- else execute(0x6021); //long command 4key } shift=false; superShift=false; } }
Execute
Here is where the IR library comes into the picture. This function uses the IR sender I created at the beginning of the program to send the code it receives. The protocol name and number of bits was determined experimentally by first using the IR library to detect the output of all my buttons. This detection process retrieves not only the code that needs to be sent for a specific action but also the protocol name and number of bits which is expected to stay consistent for all communications from the respective remote.
void execute(uint32_t code){ Serial.println (code); mySender.send(SONY,code, 15);//use mySender to send the code with SONY encoding 15 bits }
Lock and Unlock
Booth functions serve to keep the corresponding signals high for a predetermined period of time. This will probably need to be refined by testing when it is mounted on the car but for the moment I set this duration to 100ms.
void lock(){ Serial.println("Locking doors"); digitalWrite(doorLock, HIGH); delay(doorDelay); digitalWrite(doorLock, LOW); Serial.println("Doors locked"); } void unlock(){ Serial.println("Unlocking doors"); digitalWrite(doorUnlock, HIGH); delay(doorDelay); digitalWrite(doorUnlock, LOW); Serial.println("Doors unlocked"); }
And that is all. All together it adds up to what I hope is a functional interface between the controls and Sony unit.