Force Feedback With LCD Screen
by Progressive Automations in Circuits > Arduino
14646 Views, 136 Favorites, 0 Comments
Force Feedback With LCD Screen
In today's Instructable, we'll be going over how to monitor the current of an actuator, and adjust the values using an LCD screen. This Instructable is a continuation to the Monitoring the Load Feedback of an Actuator Instructable. We'll be adding in the LCD screen so you can see how much current the actuator is pulling, as well as set the current limits.
First, we'll go over the LCD buttons and how they integrate with the Arduino, and then go on to see how we can write and set values to the LCD screen so we can see what the program is doing.
For this project, you will need:
- 1x MegaMoto
- 1x Arduino Uno
- 1x Linear Actuator
- 1x LCD Screen
- 1x 12V Power Supply
Let's get started!
LCD Screen Buttons
The LCD screen has six buttons on it, five that are available for use. We'll be using four of them for our project. The buttons are Up, Down, Left, Right, Select, and Reset. The Reset button resets the entire Arduino. We'll use the four directional buttons. Up and Down will set the current limit, Left and Right will move the actuator.
The LCD screen puts all five buttons through a Digital to Analog Converter so that they only take up one pin. The flip side to this is that you need to calibrate your buttons.
The easiest way to do so is to use the Arduino AnalogReadSerial example, and see what value you get for each button press. Most LCD screens use pin A0 for the buttons. Be sure to change the delay value in AnalogReadSerial so that you can actually see what values appear in the serial monitor. There are six readings that you need to get, the five readings of the buttons, plus the reading that you get when no buttons are pressed.
Once you have the readings of the buttons, you can use the code here to ensure that they are all working. One more value that is used is the threshold. The threshold is to ensure that when you press the button, it will actually register. Since the button presses go through an DAC, they may be slightly different each press. You use the threshold value to give a +/- addition so that the button presses can fall within a range, rather then one exact value. Usually, a small threshold like 2 or 3 is enough for a single board, but if you want to make a program that will work with many different LCD boards (for example, you're making 10 copies of your project) then you would use a larger threshold to account for the slight differences in the boards.
Extra comments have been added to the code so you can see what everything is for. Once you've tested the code and gotten all your button values, we can move on to the next program.
//Sample using LiquidCrystal library #include <LiquidCrystal.h> /******************************************************* This program will test the LCD panel and the buttons Mark Bramwell, July 2010 ******************************************************** // select the pins used on the LCD panel LiquidCrystal lcd(8, 9, 4, 5, 6, 7); //rs, enable, d4,d5,d6,d7 // define some values used by the panel and buttons int lcd_key = 0;//which button was pressed int adc_key_in = 0;//what was the input of the DAC #define btnRIGHT 0 #define btnUP 1 #define btnDOWN 2 #define btnLEFT 3 #define btnSELECT 4 #define btnNONE 5 //set button return values const int btnup = 130; const int btndown = 306; const int btnleft = 479; const int btnright = 0; const int select = 720; const int none = 127; //Put your button values here const int threshold = 2; //Put threshold value here int read_LCD_buttons() { adc_key_in = analogRead(A0); // read the value from the sensor if (adc_key_in > none - threshold && adc_key_in < none + threshold) return btnNONE; // We make this the 1st option for speed reasons since it will be the most likely result if (adc_key_in > btnup - threshold && adc_key_in < btnup + threshold) return btnUP; if (adc_key_in > btndown - threshold && adc_key_in < btndown + threshold) return btnDOWN; if (adc_key_in > btnleft - threshold && adc_key_in < btnleft + threshold) return btnLEFT; if (adc_key_in > btnright - threshold && adc_key_in < btnright + threshold) return btnRIGHT; if (adc_key_in > select - threshold && adc_key_in < select + threshold) return btnSELECT; //if else statements could be used, but since each statement uses return, you don't lose speed for using all if's return btnNONE; // when all others fail, return this... }//end read_LCD_Buttons void setup() { lcd.begin(16, 2); // start the library lcd.setCursor(0,0); // Set cursor to upper left corner lcd.print("Push the buttons"); // print a simple message }//end setup void loop() { lcd.setCursor(9,1); // move cursor to second line "1" and 9 spaces over lcd.print(millis()/1000); // display seconds elapsed since power-up lcd.setCursor(0,1); // move to the begining of the second line lcd_key = read_LCD_buttons(); // read the buttons switch (lcd_key) // depending on which button was pushed, we perform an action { case btnRIGHT: { lcd.print("RIGHT "); break; } case btnLEFT: { lcd.print("LEFT "); break; } case btnUP: { lcd.print("UP "); break; } case btnDOWN: { lcd.print("DOWN "); break; } case btnSELECT: { lcd.print("SELECT"); break; } case btnNONE: { lcd.print("NONE "); break; } }//end switch }//end loop
Downloads
Connect to MegaMoto
First, we need to wire the MegaMoto. Do the following:
- Connect your 12V supply to the BAT+/- terminals
- Connect the actuator to the MOTA and MOTB terminals
- OPTIONAL Connect BAT+ to Vin if you want to power the Arduino away from the computer, or leave the USB plugged in.
- Set the MegaMoto pins as follows:
- Enable = 13
- PWMA = 11
- PWMB = 3
- Sensor = A5
The left button should make the actuator extend, the right button should make the actuator retract. If your directions are reversed, then reverse the MOTA and MOTB connections.
Now that the buttons are configured we can add the LCD program to the rest of the program. We've added four new functions. Read_LCD_buttons(), updateTrip(), updateLCD() and printFeedback().
Read_LCD_buttons() is the same as the test program, it reads the status of the buttons from the LCD screen.
UpdateTrip() is used to check the results of the Up and Down buttons, and change the amp limit to trip the safety shutdown at.
UpdateLCD() is the general LCD writing function, that writes all the new information to the LCD screen.
PrintFeedback() writes the current amperage draw to the LCD screen. This is separate from the main updateLCD() function, so that we can see the current draw in real time, rather then waiting for the program to loop.
Upload the program to test is, in the next step we'll be going over the different sections of the code in detail.
NOTE: The trip limits and current feedback aren't actually the number of amps that are being drawn, they are just the results of the current sensor. You can add an equation in to convert the value to amps before you write it to the LCD screen.
/* Code to display the current amp draw of the actuator, and to cut power if it rises above a certain amount. Written by Progressive Automations July 16th, 2015 Hardware: - RobotPower MegaMoto control boards - LCD shield - Arduino Uno */ #include <LiquidCrystal.h> LiquidCrystal lcd(8, 9, 4, 5, 6, 7);// select the pins used on the LCD panel // define some values used by the panel and buttons #define btnRIGHT 1 #define btnUP 2 #define btnDOWN 3 #define btnLEFT 4 #define btnSELECT 5 #define btnNONE 0 int lcd_key = 0; int adc_key_in = 0;//value to read LCD button input const int btnup = 130; const int btndown = 306; const int btnleft = 479; const int btnright = 0; const int select = 720; const int none = 1023; const int threshold = 2;//Analog values for LCD buttons const int EnablePin = 13; const int PWMPinA = 11; const int PWMPinB = 3; // pins for Megamoto const int CPin1 = A5; // motor feedback int button = 0;//value to read LCD buttons int leftlatch = LOW; int rightlatch = LOW;//motor latches int hitLimits = 0; int hitLimitsmaxfront = 10; int hitLimitsmaxback = 5;//values to know if travel limits were reached long lastfeedbacktime = 0; int firstfeedbacktimedelay = 750; //first delay to ignore current spike int feedbacktimedelay = 50; //delay between feedback cycles long currentTimefeedback = 0; int debounceTime = 500; //amount to debounce long lastButtonpress = 0; // timer for debouncing long currentTimedebounce = 0; int CRaw = 0; // raw A/D value int maxAmps = 0; // trip limit long currentTimetrip = 0; long lastUpdate = 0; int updateTime = 100;//how fast can trip values update bool dontExtend = false; bool firstRun = true;//program logic char* title[] = {" PROGRESSIVE", " AUTOMATIONS"}; //Power on title void setup() { Serial.begin(9600); pinMode(EnablePin, OUTPUT); pinMode(PWMPinA, OUTPUT); pinMode(PWMPinB, OUTPUT);//Set motor outputs pinMode(CPin1, INPUT);//set feedback input lcd.begin(16, 2);// start the library lcd.setCursor(0, 0), lcd.print(title[0]), lcd.setCursor(0, 1), lcd.print(title[1]); //Print title delay(2000);//show title for 2 seconds currentTimetrip = millis(); currentTimedebounce = millis(); currentTimefeedback = 0;//Set initial times lcd.setCursor(0, 0); lcd.print("Amps Feedback"); // print the menu message updateLCD(); //write new values to the screen }//end setup void loop() { //Serial.println("Main loop"); updateTrip();//check buttons, update amperage latchButtons();//check buttons, see if we need to move moveMotor();//check latches, move motor in or out }//end main loop void updateLCD() { lcd.setCursor(0, 1); lcd.print("Amps: 0"); lcd.setCursor(8, 1); lcd.print("Trip: 0"); lcd.setCursor(13, 1); lcd.print(" ");//clear old value if (maxAmps > 99) lcd.setCursor(13, 1); else if (maxAmps > 9) lcd.setCursor(14, 1); else lcd.setCursor(15, 1); lcd.print(maxAmps);//write new value }//end updateLCD void latchButtons() { button = read_LCD_buttons(); if (button == btnLEFT && rightlatch != HIGH)//left is forwards { currentTimedebounce = millis() - lastButtonpress; if (currentTimedebounce > debounceTime && dontExtend == false)//once you've tripped dontExtend, ignore all forwards presses { leftlatch = !leftlatch; button = btnNONE; firstRun = true; lastButtonpress = millis(); return; }//end if }//end btnLEFT if (button == btnRIGHT && leftlatch != HIGH)//right is backwards { currentTimedebounce = millis() - lastButtonpress; if (currentTimedebounce > debounceTime) { rightlatch = !rightlatch; button = btnNONE; firstRun = true; lastButtonpress = millis(); return; }//end if }//end btnRIGHT }//end latchButtons int read_LCD_buttons() { adc_key_in = analogRead(A0); // read the value from the sensor delay(20); if (adc_key_in > none - threshold && adc_key_in < none + threshold) return btnNONE; if (adc_key_in > btnup - threshold && adc_key_in < btnup + threshold) return btnUP; if (adc_key_in > btndown - threshold && adc_key_in < btndown + threshold) return btnDOWN; if (adc_key_in > btnleft - threshold && adc_key_in < btnleft + threshold) return btnLEFT; if (adc_key_in > btnright - threshold && adc_key_in < btnright + threshold) return btnRIGHT; if (adc_key_in > select - threshold && adc_key_in < select + threshold) return btnSELECT; return btnNONE; // when all others fail, return none }//end readLCD void updateTrip() { if (button == btnUP) { currentTimetrip = millis() - lastUpdate; if (currentTimetrip > updateTime) { maxAmps = maxAmps + 1; if (maxAmps > 999) maxAmps = 0;//check for rollover lcd.setCursor(13, 1); lcd.print(" ");//clear old value if (maxAmps > 99) lcd.setCursor(13, 1); else if (maxAmps > 9) lcd.setCursor(14, 1); else lcd.setCursor(15, 1);//set cursor accordingly lcd.print(maxAmps);//write new value lastUpdate = millis(); }//end if }//end if btnUP else if (button == btnDOWN) { currentTimetrip = millis() - lastUpdate; if (currentTimetrip > updateTime) { maxAmps = maxAmps - 1; if (maxAmps < 0) maxAmps = 999;//check for rollover lcd.setCursor(13, 1); lcd.print(" ");//clear old value if (maxAmps > 99) lcd.setCursor(13, 1); else if (maxAmps > 9) lcd.setCursor(14, 1); else lcd.setCursor(15, 1);//set cursor accordingly lcd.print(maxAmps);//write new value lastUpdate = millis(); }//end if }//end else if btnDOWN }//end updateTrip void moveMotor() { if (leftlatch == HIGH) motorForward(255); //speed = 0-255 if (leftlatch == LOW) motorStop(); if (rightlatch == HIGH) motorBack(255); //speed = 0-255 if (rightlatch == LOW) motorStop(); }//end moveMotor void motorForward(int speeed) { while (dontExtend == false && leftlatch == HIGH) { //Serial.println("Moving forwards"); digitalWrite(EnablePin, HIGH); analogWrite(PWMPinA, speeed); analogWrite(PWMPinB, 0);//move motor if (firstRun == true) delay(firstfeedbacktimedelay); else delay(feedbacktimedelay); //small delay to get to speed getFeedback(); firstRun = false; latchButtons(); }//end while }//end motorForward void motorBack (int speeed) { while (rightlatch == HIGH) { //Serial.println("Moving Back"); digitalWrite(EnablePin, HIGH); analogWrite(PWMPinA, 0); analogWrite(PWMPinB, speeed);//move motor delay(3); //small delay to get to speed currentTimefeedback = millis() - lastfeedbacktime; if (firstRun == true && currentTimefeedback > firstfeedbacktimedelay) getFeedback(); //if it hasnt been long enough, do nothing if (firstRun == false && currentTimefeedback > feedbacktimedelay) getFeedback(); //if it hasnt been long enough, do nothing firstRun = false; latchButtons(); }//end while dontExtend = false; }//end motorBack void motorStop() { analogWrite(PWMPinA, 0); analogWrite(PWMPinB, 0); digitalWrite(EnablePin, LOW); firstRun = true;//once the motor has stopped, reenable firstRun to account for startup current spikes }//end stopMotor void getFeedback() { CRaw = analogRead(CPin1); if (CRaw == 0 && hitLimits < hitLimitsmaxback) hitLimits = hitLimits + 1; else if (CRaw == 0 && hitLimits < hitLimitsmaxfront) hitLimits = hitLimits + 1; else hitLimits = 0; if (hitLimits == hitLimitsmaxback) { rightlatch = LOW; }//end if if (hitLimits == hitLimitsmaxfront) { leftlatch = LOW; hitLimits = 0; }//end if if (CRaw > maxAmps) { dontExtend = true; leftlatch = LOW; }//end if printFeedback(); lastfeedbacktime = millis();//store previous time for receiving feedback }//end getFeedback void printFeedback() { lcd.setCursor(5, 1); lcd.print(" "); if (CRaw > 99) lcd.setCursor(5, 1); else if (CRaw > 9) lcd.setCursor(6, 1); else lcd.setCursor(7, 1); lcd.print(CRaw); }//end printFeedback
Downloads
Detailed Program Overview
Here we will go over the four new functions that were added, and explain each one. If the other parts of the code confuse you, check the Monitoring the Load Feedback Instructable for a detailed explanation of the rest of the code.
First, the read_LCD_buttons() function. This function does an analogRead() on the button pin of the LCD screen, and compares the value that it reads to the preset button values. If the value falls within the programmed value (plus or minus the threshold), then it returns which button was pressed. If it fails to match one of the programmed values, it returns as no button being pressed.
adc_key_in = analogRead(A0); // read the value from the sensor delay(20); if (adc_key_in > none - threshold && adc_key_in < none + threshold) return btnNONE; if (adc_key_in > btnup - threshold && adc_key_in < btnup + threshold) return btnUP; if (adc_key_in > btndown - threshold && adc_key_in < btndown + threshold) return btnDOWN; if (adc_key_in > btnleft - threshold && adc_key_in < btnleft + threshold) return btnLEFT; if (adc_key_in > btnright - threshold && adc_key_in < btnright + threshold) return btnRIGHT; if (adc_key_in > select - threshold && adc_key_in < select + threshold) return btnSELECT; return btnNONE; // when all others fail, return none
Second, the updateTrip() function. This checks the Up and Down buttons, and increases or decreases the trip limit accordingly. It has debouncing as well, so the trip value can't update too fast. You can adjust updateTime to change how fast the trip values update. You can hold down the button to change trip values quickly. By changing updateTime, you can change how fast (or slow) the trip value changes when you hold the Up or Down button down. If the value hits 0, and you continue to go down, it will roll over to 999. If you hit 999 and continue up, it will roll over to 0.
if (button == btnUP) { currentTimetrip = millis() - lastUpdate; if (currentTimetrip > updateTime) { maxAmps = maxAmps + 1; if (maxAmps > 999) maxAmps = 0;//check for rollover lastUpdate = millis(); }//end if }//end if btnUP else if (button == btnDOWN) { currentTimetrip = millis() - lastUpdate; if (currentTimetrip > updateTime) { maxAmps = maxAmps - 1; if (maxAmps < 0) maxAmps = 999;//check for rollover lastUpdate = millis(); }//end if }//end else if btnDOWN
Third is the updateLCD() function. This writes new values to the LCD screen, so you can see what is happening in your program. It uses setCursor() to move the cursor around the screen, and put the messages in the correct place. the first value in setCursor() is the row position that the cursor is in, the second value is whether the cursor is in the upper or lower row. Since the trip limit can be between 0-999, the cursor moves to different spots depending if it is a 1, 2 or 3 digit value. There are spaces printed (" ") that are used to clear the spot where the number is printed. If the spot was not cleared, when you change from a 2 digit value down to a 1 digit value, the second digit won't get cleared when the first digit is updated, meaning that your LCD screen would display weird numbers.
lcd.setCursor(0, 1); lcd.print("Amps: 0"); lcd.setCursor(8, 1); lcd.print("Trip: 0"); lcd.setCursor(13, 1); lcd.print(" ");//clear old value if (maxAmps > 99) lcd.setCursor(13, 1); else if (maxAmps > 9) lcd.setCursor(14, 1); else lcd.setCursor(15, 1); lcd.print(maxAmps);//write new value
Last is the printFeedback() function. It is very similar to the updateLCD() function, it is just separate so that the value of the current amp draw is updated in real time, rather then once every time the program loops. This function is called to update the amp draw whenever the getFeedback() function is called to read what the amp draw is.
lcd.setCursor(5, 1); lcd.print(" "); if (CRaw > 99) lcd.setCursor(5, 1); else if (CRaw > 9) lcd.setCursor(6, 1); else lcd.setCursor(7, 1); lcd.print(CRaw);
Conclusion
In this Instructable, we learned how an LCD screen can be used to adjust parameters of your program in real time. We used it as an extension to the Monitoring the Load Feedback Instructable to adjust the current limits for the actuator to stop at. An addition to this program would be to add the EEPROM library, so that each time you power the system on and off the values you set are saved.
If you'd like to take a look at our selection of linear actuators, motions control systems and microcontrollers then please visit us at www.progressiveautomations.com for all your actuator needs! We can even build a custom actuator or control system for you based on your own custom specifications with the help of our highly trained staff of engineers. You can learn more about the custom order process right here!
Follow, Like and Subscribe!
Twitter - twitter.com/ProgAutoInc
Facebook - www.facebook.com/ProgressiveAutomations
Youtube - www.youtube.com/user/MrActuators