Force Feedback with LCD Screen

Written by: admin@makezilla

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!

Step 1: 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 buttonsMark Bramwell, July 2010********************************************************// select the pins used on the LCD panelLiquidCrystal lcd(8, 9, 4, 5, 6, 7); //rs, enable, d4,d5,d6,d7// define some values used by the panel and buttonsint lcd_key     = 0;//which button was pressedint 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 valuesconst 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 hereconst int threshold = 2; //Put threshold value hereint 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 resultif (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_Buttonsvoid 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

http://www.instructables.com/files/orig/FI6/HABA/IF7IVP67/FI6HABAIF7IVP67.ino

Step 2: 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   0int lcd_key     = 0;int adc_key_in  = 0;//value to read LCD button inputconst 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 buttonsconst int EnablePin = 13;const int PWMPinA = 11;const int PWMPinB = 3; // pins for Megamotoconst int CPin1 = A5;  // motor feedbackint button = 0;//value to read LCD buttonsint leftlatch = LOW;int rightlatch = LOW;//motor latchesint hitLimits = 0;int hitLimitsmaxfront = 10;int hitLimitsmaxback = 5;//values to know if travel limits were reachedlong lastfeedbacktime = 0;int firstfeedbacktimedelay = 750; //first delay to ignore current spikeint feedbacktimedelay = 50; //delay between feedback cycleslong currentTimefeedback = 0;int debounceTime = 500; //amount to debouncelong lastButtonpress = 0; // timer for debouncinglong currentTimedebounce = 0;int CRaw = 0;      // raw A/D valueint maxAmps = 0; // trip limitlong currentTimetrip = 0;long lastUpdate = 0;int updateTime = 100;//how fast can trip values updatebool dontExtend = false;bool firstRun = true;//program logicchar* title[] = {"  PROGRESSIVE", "  AUTOMATIONS"};        //Power on titlevoid 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 setupvoid 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 loopvoid 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 updateLCDvoid 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 latchButtonsint 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 readLCDvoid 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 updateTripvoid 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 moveMotorvoid 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 motorForwardvoid 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 motorBackvoid 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 stopMotorvoid 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 getFeedbackvoid 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

http://www.instructables.com/files/orig/FEW/HCF6/IF7IVP66/FEWHCF6IF7IVP66.ino

Step 3: 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 sensordelay(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);

Step 4: 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 atwww.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/ACTUATORS1

Facebook - www.facebook.com/ProgressiveAutomations

Youtube - www.youtube.com/user/MrActuators

Leave a Reply