An improved key-reader library for the SainSmart LCD Keypad Shield (for Arduino)

The LCD Keypad shield from SainSmart is a bargain. For ~$8 (and free shipping, at the time of this post) you get 32 character (16×2) blue/white LCD panel with a built-in keypad with 5 momentary switches. This provides a quick solution for interaction with Arduino projects. To control the LCD part of the shield, one can use the Arduino supplied LiquidCrystal library (included with Arduino IDE). In the Key_Grab demo (v. 0.2) on the SainSmart website is included a library, DFR_Key, for reading which keys are pressed on the keypad. However, the library is not written well. This post describes both the problem and the solution.

First, a little background is necessary about how the keypad signals a key is pressed. Only a single analog-in pin is used and the shield uses a voltage divider to send a voltage that corresponds to a given key. To detect a key, one has only to read the voltage at the analog pin and determine if it falls within a certain range. For example, a voltage of 0 to 0.7 V signals the UP key was pressed. The DFR_Key library contains a getKey() function that does this and returns an integer from 0 to 5 that represents the key pressed (0 is supposed to mean no key, but as written means the last key was just released).

You’d expect that when you call getKey() it would read the voltage at the pin, figure out what key that represented, and send back the integer representing the particular key. So really, it should just be translating the voltage to an integer from 0 to 5 (none, select, left, up, down, right, respectively). But, there is some “extra functionality” the library provides that is the problem. Here’s how the logic of the getKey() function works:

Has a minimum time passed since the last time I checked for a key press?

If not, do nothing – return no value at all!

If so, read the voltage at the pin. Is this voltage different than the voltage I read the last time I checked for a keypress?

If not, return -1.

If they’re different, it means a key was pressed right? Determine which key was pressed and return an integer from 0 to 5 representing that key.

The first bit of “extra functionality” is what the library refers to as the refreshRate. The idea is that you don’t want to check for a key press too quickly, else the function might return that the key is being held down when in fact it’s just a normal press. This makes some sense if you simply dump a getKey() call in your Arduino sketch’s loop() function. You would then check the returned value to see if it actually did a key press. If it decided enough time hadn’t pass it would return….oh wait! It doesn’t return anything in this case. That’s not appropriate behavior. When the program says getKey(), it should expect a key code sent back. Therefore, it should be the job of the programmer to only call getKey() at appropriate intervals (say every 100 ms).

That brings up the next problem. There are five keys on the keypad. Add in an extra code for “no key being pressed” and you get the six integers from 0 to 5. Perfectly reasonable. So what’s this -1 return value for? The library defines the alias SAMPLE_WAIT for this value (as opposed to NO_KEY, and UP_KEY, etc.). I’m not sure what the meaning was supposed to be, but this value is returned when the getKey() function determines that the key being pressed is the same as the key that was detected upon the last call to getKey(). At first, it would seem to be letting you know that the last key pressed is being held down. But 1: you don’t know what the last key pressed was, unless you’re tracking it yourself. And 2: that may not even be true – if you make two getKey() calls 60 s apart, whose to say whether the key was being held the whole time; all we know is that the same key was pressed as last time. So it’s telling us the same key was pressed as before and according to point 1, I’m keeping track of what that key was. If it had just returned the actual key code, I could have figured this out for myself by comparing it to my stored value of the last key pressed.

Now to the final problem. If you follow the logic stated above carefully, you’ll see that to determine if the same key was pressed, the getKey() function compares the voltage read at the analog pin to the voltage it read last time it detected a key was pressed. But the voltage does not directly correspond to a given key – a key press can be signaled by a range of voltages. For example, assume on the first call to getKey() it reads a value of 200 mV. That corresponds to the UP_KEY and it returns 3. On the next call to getKey() assume it reads a value of 205 mV. The user is pressing the up key again (or holding it down) but the voltage is slightly different. The getKey() function compares the new value, 205 mV, to the old value, 200 mV, and thinks a new key is being pressed. So instead of returning -1 for “same key is being pressed” it returns 3 for “up key is being pressed”.

Actually, this should be the behavior! Upon every call, the getKey() function should just return an integer representing the key that is being pressed at the time the call is made. But because the authors meant for it to return -1 when there was no change in the key being pressed, and meant for it to be called on every loop() iteration, the Key_Grab demo flickers the screen like crazy. Actually, it works fine when the UNO board is removed from all sources of noise and interference. But put it next to a piece of electronics (e.g. your computer) and the noise causes the voltage supplied to the analog pin to fluctuate, new key presses to be registered, and the demo to update the screen on every loop() iteration. It’s the constant updating that causes the flicker which makes it appear as though you got ripped off $10.

How can we fix this mess? Actually, the fixes are simple and actually reduce the code in the library.

  1. Remove all of the logic relating to the “refreshRate”. The programmer will be forced to decide how often to call getKey().
  2. Remove the comparison of the current key press to the previous key press. The programmer can decide if the user is pressing the same key by recording the last pressed key.

Here is the derived library which I’ve called LCD_Keypad_Reader.cpp (feel free to rename). The original SainSmart DFR_Key library didn’t come with a license so I’m not putting one on this derived work either. I don’t really care what you do with this code, but it’s possible that SainSmart could impose some kind of limitations.

#include "Arduino.h" 
#include "LCD_Keypad_Reader.h" 

static int DEFAULT_KEY_PIN = 0;  
static int DEFAULT_THRESHOLD = 5; 

// The Sainsmart keypad uses a voltage divider to deliver a voltage  
// between 0 and 5 V that corresponds to the key being pressed in  
// order to use only a single input pin. The values below are from 0 to  
// 1023 because the Arduino uses a 10 bit resolution. 
static int UPKEY_ARV = 144; // 0.720 V, that's read "analogue read value" 
static int DOWNKEY_ARV = 329; // 1.645 V 
static int LEFTKEY_ARV = 505; // 2.525 V 
static int RIGHTKEY_ARV = 0; // 0 V 
static int SELKEY_ARV = 742; // 3.710 V 
static int NOKEY_ARV = 1023; // 5.115 V 

LCD_Keypad_Reader::LCD_Keypad_Reader() 
{     
  _keyPin = DEFAULT_KEY_PIN; 
  _threshold = DEFAULT_THRESHOLD; 
  _curInput = NO_KEY; 
  _curKey = NO_KEY; 
} 

int LCD_Keypad_Reader::getKey() 
{ 
  _curInput =  analogRead(_keyPin); 
  _curKey = categorizeKey(_curInput); 
  return _curKey; 
} 

int LCD_Keypad_Reader::categorizeKey(int analogKeyValue){ 
  int categorizedKeyValue = 0; 

  if (analogKeyValue > UPKEY_ARV - _threshold && analogKeyValue < UPKEY_ARV + _threshold ){ 
      categorizedKeyValue = UP_KEY; 
  } 
  else if (analogKeyValue > DOWNKEY_ARV - _threshold && analogKeyValue < DOWNKEY_ARV + _threshold ){ 
      categorizedKeyValue = DOWN_KEY; 
  } 
  else if (analogKeyValue > RIGHTKEY_ARV - _threshold && analogKeyValue < RIGHTKEY_ARV + _threshold ){ 
      categorizedKeyValue = RIGHT_KEY; 
  } 
  else if (analogKeyValue > LEFTKEY_ARV - _threshold && analogKeyValue < LEFTKEY_ARV + _threshold ){  
      categorizedKeyValue = LEFT_KEY; 
  } 
  else if (analogKeyValue > SELKEY_ARV - _threshold && analogKeyValue < SELKEY_ARV + _threshold ){ 
      categorizedKeyValue = SELECT_KEY; 
  } 
  else{ 
    categorizedKeyValue = NO_KEY; 
  } 

  return categorizedKeyValue; 
}

And here is the header file, LCD_Keypad_Reader.h

#ifndef LCD_Keypad_Reader_h 
#define LCD_Keypad_Reader_h 

#include "Arduino.h" 

#define SAMPLE_WAIT -1 
#define NO_KEY 0 
#define UP_KEY 3 
#define DOWN_KEY 4 
#define LEFT_KEY 2 
#define RIGHT_KEY 5 
#define SELECT_KEY 1 

class LCD_Keypad_Reader 
{ 
  public: 
    LCD_Keypad_Reader(); 
    int getKey(); 
    int categorizeKey(int); 
  private: 
    int _keyPin; 
    int _threshold; 
    int _curInput; 
    int _curKey; 
}; 

#endif

2 thoughts on “An improved key-reader library for the SainSmart LCD Keypad Shield (for Arduino)”

  1. Nice work! The libraries from the official website are pretty messy, you’ve made a great analysis. Out of curiosity, would you be able/willing to post a sample of your main sketch?

    1. Below is the code to a sketch I was working on for a temperature controller. I ended up destroying my thermocouple chip with my soldering iron and the project stalled. I don’t remember the state of the code. It’s a complicated piece. I was programming a menu system for the LCD. If I recall, the menu system was functional. C++ is not my usual language so you experts out there will need forgive the crudeness.

      #include 
      #include 
      #include 		// strcpy, strcat, etc.
      
      //Pin assignments for SainSmart LCD Keypad Shield
      LiquidCrystal lcd(8, 9, 4, 5, 6, 7); 
      //---------------------------------------------
      
      LCD_Keypad_Reader keypad;
      
      int localKey = 0;
      String keyString = "";
      char endChar = '\n';
      char state[5] = "OFF";	// OFF, TIME (for dwelling), RATE (for ramping), HOLD (for holding)
      char heldState[5];
      char homeTextRow1[17] = "CURR  TRGT   OFF";		// will be like "CURR  TRGT  TIME"
      char homeTextRow2[17] = ""; 	// will be like: "999K  999K 9999s"
      int currentTemp = 99;
      int intermediateTargetTemp = 0;	// current actual target
      int targetTemp = 100;	// set point desired by user
      int currentPower = 60;	// 0 to 100, % of each heating cycle the heater is on
      int heatingCycleDuration = 5000;	// number of ms representing a full heating cycle
      unsigned long cycleCounter = 0;	// stores time since last heating cycle
      int dwellTimeRemaining = 0;		// in seconds
      unsigned long secondCounter = 0;
      const int keyRepeatRate = 100;	// when held, key repeats 1000 / keyRepeatRate times per second
      unsigned long lastKeyCheckTime = 0;
      int lastKeyPressed = 0;
      const int keySampleRate = 10; // ms between checking keypad for key
      unsigned long lastKeyPressTime = 0;
      int sensorPin = A1;   
      
      
      typedef struct{
      	char name[17];	// 16 chars plus null
      	char value[9];	// if choice, second index in options array below (8 chars plus null)
      	char type[7];	// choice, int, dec
      	int limit;		// only for numeric types, maximum value allowed
      } Setting;
      const int numberOfSettings = 5;	// change as more settings are added
      // if any of these settings change position, it will affect hard-coded indices in below code
      Setting settings[] = {
      	{"CONTROL MODE", "0", "choice"},
      	{"DWELL TIME", "10", "int", 9999},
      	{"RAMP RATE", "1.0", "dec", 99},
      	{"TEMP UNITS", "0", "choice"},	
      	{"HOLD MODE", "0", "choice"}
      };
      int settingsPos = -1;
      int homeScreenPos = -1;
      
      // we are forced to declare second dimension for array which limits the max options
      const int numberOfOptions = 3;	// needs to match most number of options for any setting
      char *options[][numberOfOptions]{
      	{"AUTO", "MANUAL"},
      	{},	// DWELL TIME is not a choice
      	{}, // RAMP RATE is not a choice
      	{"K", "C", "F"},
      	{"CURR TEMP", "CURR POWER"}
      };
      
      void setup(){ 
      	Serial.begin(9600);
      	lcd.begin(16, 2);
      	refreshDisplay();
      	
      	// configure relays
      	pinMode(2, OUTPUT);
      	pinMode(3, OUTPUT);
      	digitalWrite(2, LOW);
      	digitalWrite(3, LOW);
      	
      	cycleCounter = millis();
      }
      void loop(){
      	// process any serial commands
      	if (Serial.available() > 0){
      		char serialText[32];
      		Serial.readBytesUntil(endChar, serialText, 32);
      		lcd.clear();    
      		lcd.setCursor(0, 0);
      		lcd.print(serialText);
      		if (!strcmp(serialText, "1 on")){
      			digitalWrite(2, HIGH);
      			lcd.setCursor(0,1);
      			lcd.print("ON!");
      		}
      		else if (!strcmp(serialText, "1 off")){
      			digitalWrite(2, LOW);
      			lcd.setCursor(0,1);
      			lcd.print("OFF!");
      		}
      		else if (!strcmp(serialText, "2 on")){
      			digitalWrite(3, HIGH);
      			lcd.setCursor(0,1);
      			lcd.print("ON!");
      		}
      		else if (!strcmp(serialText, "2 off")){
      			digitalWrite(3, LOW);
      			lcd.setCursor(0,1);
      			lcd.print("OFF!");
      		}
      	} 
      	
      	// check for key presses
      	if (millis() > lastKeyCheckTime + keySampleRate){
      		lastKeyCheckTime = millis();
      		localKey = keypad.getKey();
      		if (localKey != lastKeyPressed){
      			processKey(localKey);
      		} else{
      			// key value has not changed, key is being held down, has it been long enough?
      			// (but don't process localKey = 0 = no key pressed)
      			if (localKey != 0 && millis() > lastKeyPressTime + keyRepeatRate){
      				// yes, repeat this key
      				// changed: don't repeat left and right, they only change menu options
      				if (localKey != 5 && localKey != 2){
      					// and really, no sense in repeating except when chaning numeric settings
      					if (!strcmp(settings[settingsPos].type, "int") || !strcmp(settings[settingsPos].type, "dec")){
      						processKey(localKey);
      					}
      				}
      			}
      		}
      	}
      	
      	// read current temp from thermocouple
      	// since AD8495's output is 5 mV / deg C and the arduino's input is 
      	// 1 unit / 4.89 mV (5 V / 1023), conversion is approx. 1 unit = 1 deg C
      	currentTemp = analogRead(sensorPin);
      	Serial.println(currentTemp);
      	// but somethings wrong at the moment so until it's fixed:
      	//currentTemp = analogRead(sensorPin) / 5;
      	if (!strcmp(options[3][strtoint(settings[3].value)], "K")){
      		currentTemp += 273;
      	}
      	else if (!strcmp(options[3][strtoint(settings[3].value)], "F")){
      		currentTemp = (currentTemp * 9 / 5) + 32;
      	}
      	
      	// is heat ramping over?
      	if (!strcmp(state, "RATE")){
      		// is ramping finished?
      		if (currentTemp >= targetTemp){
      			// yes, enter dwell state
      			strcpy(state, "TIME");
      			dwellTimeRemaining = strtoint(settings[1].value);
      		} else{
      			// check to see if it's time to increase to the next intermediate temp 
      			// (based on ramp rate and elapsed time)
      		}
      	}
      	
      	// decrement dwell time every second
      	// and also refresh the display
      	if (millis() >= secondCounter + 1000){
      		secondCounter = millis();
      		if (!strcmp(state, "TIME")){
      			if (dwellTimeRemaining > 0){
      				dwellTimeRemaining--;
      			} else{
      				// dwell is finished
      				strcpy(state, "OFF");
      			}
      			refreshDisplay();
      		} else{
      			refreshDisplay();
      		}
      	}
      	
      	// every n ms we turn the heater relay on once for 0.0 to n ms
      	// where n is heatingCycleDuration and the duration on is determined by currentPower
      	if (millis() >= cycleCounter + heatingCycleDuration){
      		// start new heating cycle, turn heater on
      		cycleCounter = millis();
      		if (!strcmp(state, "RATE") || !strcmp(state, "TIME") || !strcmp(state, "HOLD")){
      			Serial.println("relay on!");
      			digitalWrite(2, HIGH);
      		}
      	}
      	if (millis() >= cycleCounter + (currentPower/100.0 * heatingCycleDuration)){
      		// time to turn heater off (if it's on)
      		if (digitalRead(2)){
      			Serial.println("relay off!");
      			digitalWrite(2, LOW);
      		}
      	}
      }
      
      void processKey(int localKey){
      	lastKeyPressed = localKey;
      	lastKeyPressTime = millis();
      	// right
      	if (localKey == 5){
      		// old technique to get size of array of strings (saved in case useful)
      		//int numberOfMenuItems = sizeof(menus) / sizeof(*menus);
      		if (settingsPos < numberOfSettings- 1){
      			settingsPos++;
      		}
      	}
      	// left
      	if (localKey == 2){
      		if (settingsPos != homeScreenPos){
      			settingsPos--;
      		} else{
      			// at the home screen, and state != off, left puts the system in/out of hold mode
      			if (strcmp(state, "OFF")){
      				if (strcmp(state, "HOLD")){
      					strcpy(heldState, state);
      					strcpy(state, "HOLD");
      				} else{
      					strcpy(state, heldState);
      				}
      			}
      		}
      	}
      	// up
      	if (localKey == 3){
      		if (settingsPos != homeScreenPos){
      			if (!strcmp(settings[settingsPos].type,"choice")){
      				// for a "choice", select the next possible choice if one exists
      				int optionPos =  strtoint(settings[settingsPos].value);
      				if (optionPos < lastUsedOptionPos()){
      					optionPos++;
      					sprintf(settings[settingsPos].value, "%d", optionPos);
      				}
      			}
      			else if (!strcmp(settings[settingsPos].type,"int")){
      				// for numeric types, we need to increment the value
      				int value = strtoint(settings[settingsPos].value);
      				if (value < settings[settingsPos].limit){
      					value++;
      				}
      				sprintf(settings[settingsPos].value, "%d", value);
      			}
      			else if (!strcmp(settings[settingsPos].type,"dec")){
      				double value = strtod(settings[settingsPos].value, (char **)NULL);
      				if (value < settings[settingsPos].limit){
      					changeCurrentDecimalSetting(1);	// increment tenths place of decimal by 1
      				}
      			}
      		} else{
      			// at the home screen, up/down changes the target temp
      			if (targetTemp < 9999){
      				targetTemp++;
      			}
      		}
      	}
      	// down
      	if (localKey == 4){
      		if (settingsPos != homeScreenPos){
      			if (!strcmp(settings[settingsPos].type,"choice")){
      				// for a "choice", select the previous choice if one exists
      				int optionPos =  strtoint(settings[settingsPos].value);
      				if (optionPos > 0){
      					optionPos--;
      					sprintf(settings[settingsPos].value, "%d", optionPos);
      				}
      			}
      			else if (!strcmp(settings[settingsPos].type,"int")){
      				// for numeric types, we need to decrement the value
      				int value = strtoint(settings[settingsPos].value);
      				if (value > 0){
      					value--;
      				}
      				sprintf(settings[settingsPos].value, "%d", value);
      			}
      			else if (!strcmp(settings[settingsPos].type,"dec")){
      				changeCurrentDecimalSetting(-1);
      			}
      		} else{
      			// at the home screen, up/down changes the target temp
      			if (targetTemp > 0){
      				targetTemp--;
      			}
      		}
      	}
      	// select
      	if (localKey == 1){
      		if (settingsPos == homeScreenPos){
      			// if on the home screen, this key changes the state from off, to on (dwell or ramp), to off again
      			if (!strcmp(state, "OFF")){
      				// start ramping
      				strcpy(state, "RATE");
      			}
      			else if (strcmp(state, "OFF")){
      				// any other state but OFF, turn off
      				strcpy(state, "OFF");
      			}
      		} else{
      			// when not on the home screen, this key returns the display to the home screen
      			settingsPos = homeScreenPos;
      		}
      	}
      	
      	refreshDisplay();
      }
      
      void changeCurrentDecimalSetting(int amountToChange){
      		// this is a special decimal type, we'll increment the tenths place and if
      		// necessary the number to the left of the decimal
      		char *value = settings[settingsPos].value;
      		char tenthsChar[1] = {(char)value[strsize(value) - 1]};	// last character is tenths place
      		int tenths = strtoint(tenthsChar);
      		char leftDigitsChar[9];
      		// the number of digits to the left of the decimal is the string size - 2 (for ".0", etc.)
      		strncpy(leftDigitsChar, value, strsize(value) - 2);
      		leftDigitsChar[strsize(value) - 2] = '\0';	// have to manually add null to terminate string
      		int leftDigits = strtoint(leftDigitsChar);
      		
      		tenths += amountToChange;
      		if (tenths > 9){
      			tenths = 0;
      			leftDigits++;
      		}
      		else if (tenths < 0){
      			tenths = 9;
      			leftDigits--;
      			// but don't go under 0, keep everything positive
      			if (leftDigits < 0){
      				leftDigits = 0;
      				tenths = 0;
      			}
      		}
      		
      		sprintf(leftDigitsChar, "%d", leftDigits);
      		sprintf(tenthsChar, "%d", tenths);
      		strcpy(settings[settingsPos].value,leftDigitsChar);
      		strcat(settings[settingsPos].value, ".");
      		strcat(settings[settingsPos].value, tenthsChar);
      }
      
      void refreshDisplay(){
      	setHomeTextRow1();
      	setHomeTextRow2();
      	if (settingsPos == homeScreenPos){
      		displayHomeScreen();
      	} else{
      		displaySettingScreen();
      	}
      }
      void displayHomeScreen(){
      	lcd.clear();
      	lcd.setCursor(0, 0);
      	lcd.print(homeTextRow1);
      	lcd.setCursor(0, 2);
      	lcd.print(homeTextRow2);
      }
      void displaySettingScreen(){
      	lcd.clear();
      	lcd.setCursor(0,0);
      	lcd.print(settings[settingsPos].name);
      	
      	char *value;
      	if (!strcmp(settings[settingsPos].type,"choice")){
      		// "value" is the corresponding index in the options array
      		int index = strtoint(settings[settingsPos].value);
      		value = options[settingsPos][index];
      	}
      	else {
      		// "value" is the actual value
      		value = settings[settingsPos].value;
      	}
      	
      	int cursorPos = 16 - strsize(value);
      	lcd.setCursor(cursorPos,1);
      	lcd.print(value);
      }
      void setHomeTextRow1(){
      	strcpy(homeTextRow1, "CURR  TRGT  ");
      	strcat(homeTextRow1, state);
      }
      void setHomeTextRow2(){
      	char currentTempChar[5];
      	sprintf(currentTempChar, "%d", currentTemp);
      	prependSpaces(currentTempChar, 4);
      	
      	char targetTempChar[5];
      	sprintf(targetTempChar, "%d", targetTemp);
      	prependSpaces(targetTempChar, 4);
      	
      	char units[2];
      	strcpy(units, options[3][strtoint(settings[3].value)]);
      	
      	strcpy(homeTextRow2, currentTempChar);
      	strcat(homeTextRow2, units);
      	strcat(homeTextRow2, " ");
      	strcat(homeTextRow2, targetTempChar);
      	strcat(homeTextRow2, units);
      	strcat(homeTextRow2, " ");
      	
      	if (!strcmp(state, "RATE")){
      		char rateChar[5];
      		strcpy(rateChar, settings[2].value);
      		prependSpaces(rateChar, 4);
      		strcat(homeTextRow2, rateChar);
      	}
      	else if (!strcmp(state, "TIME")){
      		char timeChar[5];
      		sprintf(timeChar, "%d", dwellTimeRemaining);
      		prependSpaces(timeChar, 4);
      		strcat(homeTextRow2, timeChar);
      	}
      }
      
      void prependSpaces(char* string, int length){
      	// this function adds spaces to the beginning of string until it is 
      	// the desired length
      	char buffer[17];
      	while (strsize(string) < 4){
      		strcpy(buffer, " ");
      		strcat(buffer, string);
      		strcpy(string, buffer);
      	}
      }
      int strtoint(char* string){
      	return (int) strtol(string, (char **)NULL, 10);
      }
      int strsize(char* string){
      	int terminatorFound = 0;
      	int pos = -1;
      	while (terminatorFound == 0) {
      		pos++;
      		if (string[pos] == '\0'){
      			terminatorFound = 1;
      		} else{
      			// don't let this loop run away!
      			if (pos > 255){
      				pos = -1;	// will return -1 on error
      				terminatorFound = 1;
      			}
      		}
      	}
      	return pos;
      }
      int lastUsedOptionPos(){
      	// this function returns the number of elements from the beginning
      	// of the array up to the last non-null element
      	// useful for multidimensional arrays where dimensions past the first 
      	// must be the same for all elements of the first dimension even 
      	// if they're not being used
      	int pos = -1;
      	int numberOfOptions = sizeof(options[settingsPos]) / sizeof(*options[settingsPos]);
      	// work backwards from end of array looking for first non-null element
      	for (int i = 0; i < numberOfOptions; i++){
      		if (options[settingsPos][i] != '\0'){
      			pos = i;
      		}
      	}
      	return pos;
      }
      
      // this will be useful later
      /*
      // Buffer to store incoming commands from serial port
      String inData;
      
      void setup() {
          Serial.begin(9600);
          Serial.println("Serial conection started, waiting for instructions...");
      }
      
      void loop() {
          while (Serial.available() > 0)
          {
              char recieved = Serial.read();
              inData += recieved; 
      
              // Process message when new line character is recieved
              if (recieved == '\n')
              {
                  Serial.print("Arduino Received: ");
                  Serial.print(inData);
      
                  // You can put some if and else here to process the message juste like that:
      
                  if(inData == "+++\n"){ // DON'T forget to add "\n" at the end of the string.
                    Serial.println("OK. Press h for help.");
                  }   
      
      
                  inData = ""; // Clear recieved buffer
              }
          }
      }
      */

Leave a Reply to Eric Cancel reply

Your email address will not be published. Required fields are marked *