Extra-simple example, free serial text match tool.

This is a Very Simplified version of the simple serial text match tool. No library files. One match word.
Serial monitor gets debug-level feedback, enter user text and match the first word then the next.
Make errors, see what it does.

1 MatchWord object can check 1 match word, a MatchWord array can cover many. All can point to different words stored in flash, your app can change context with state.

// Match_Simple.ino ver 1.0 by GoForSmoke @ Arduino Forum
// Free for use, Feb 7th, 2016
//
/*  This example uses serial monitor to get user text entry.
The example takes serial text and tries to match first "LOW" then "HIGH" alternating.
Each serial char not a delimiter gets submitted to the MatchWord object and we see
the state and characters matched before attempting to match then the resulting state.
The class used is stripped down to cut the number of things to explain some.

Regarding matching words in streaming text data, over 90% of the no-match sequences
happen in the first 2 or 3 letters.
*/

#include "Arduino.h"
#include <avr/io.h>
#include <avr/pgmspace.h>

enum MatchStates { waiting = 0, matching = 1, sub_match = 2, matched = 3, no_match = 4 };

// this example uses only a simple class example, not all done proper
typedef class MatchWord
{
  public:
    unsigned char match( char ); // takes serial char, returns state
    unsigned char whatState( )
    {
      return state;
    };
    unsigned char whatChar( )
    {
      return charsMatched;
    };
    void init( )
    {
      state = waiting;
      charsMatched = 0;
    };
    void setString( PGM_P str )
    {
      string = str;
    };
  private:
    PGM_P string; // points to text in flash
    unsigned char state; // 0=waiting, 1=matching, 2=matched, 3=sub-match, 4=no match
    unsigned char charsMatched;
};


// match states: waiting, matching, sub_match, matched, no_match

unsigned char MatchWord::match( char text ) // returns match state
{
  static char matchChar; // char read from PROGMEM, this is the buffer

  switch ( state )
  {
    case 0 : // 0 = waiting
      if ( text < '0' ) // anything less than zero is now a delimiter causes match reset
      {
        init();
        return no_match;
      }

      matchChar = pgm_read_byte( string );
      if ( matchChar == text )
      {
        charsMatched = 1;
        state = matching;
      }
      else
      {
        state = no_match;
      }
      break;

    case 1 :  //  1 = matching
      if ( text < '0' ) // anything less than zero is now a delimiter causes match reset
      {
        init();
        return no_match;
      }

      matchChar = pgm_read_byte( string + charsMatched );
      if ( matchChar == text )
      {
        charsMatched++;

        matchChar = pgm_read_byte( string + charsMatched );
        if ( !matchChar )
        {
          state = sub_match;
        }
      }
      else
      {
        state = no_match;
      }
      break;

    case 2 : // sub_match
      if ( text < '0' )
      {
        init();
        return matched;
      }
      else
      {
        state = no_match;
      }
      break;

    default :
      if ( text < '0' )
      {
        init();
      }
      break;
  }

  return state;
};


unsigned char data;

unsigned char lowHigh; // which string to match
const char keystring00[] PROGMEM  = "NAME";      // Hey! CHANGE THESE!
const char keystring01[] PROGMEM  = "PASSWORD";  // Add more words and sets of words!

PGM_P key[ 2 ] =
{
  keystring00, keystring01
};

MatchWord keyword; // keyword is a code object, a kind of variable/function hybrid.

// stat = keyword.match( char ) feeds a text char towards match/no-match of serial
// text to a string stored in flash. Knowing when no match becomes possible allows
// subsequent incoming serial text up to the next delimiter to go unchecked.

// many MatchStr objects can be used together to find whole sets key words in text.
// Just with this version, not long stretches of high speed text. Maybe later on that.

// This example only uses 1 key word at a time. It's aimed at simple.

// Since the objects' pointer to (string in flash) is in RAM, it can be changed,
// changing the key word. Perhaps with context, a command name entered, code
// may watch for named parameters to take numeric entry in turn.

// Make more MatchWord objects to watch for more than one match word at a time.


byte  printFlash( PGM_P FM )
{
  byte c;
  byte  fb; // flash byte
  c = 0;
  do
  {
    c++;
    fb = pgm_read_byte( FM++ );
    if ( fb )  Serial.print(( char ) fb );
  }
  while ( fb );
  return c;
}

void printUsage()
{
  Serial.print( F( "Match " ));
  printFlash( key[ 0 ] );
  Serial.print( F( " then " ));
  printFlash( key[ 1 ] );
  Serial.println( );
}

void setup()
{
  Serial.begin( 115200 );
  printUsage();

  keyword.setString( key[ lowHigh ] );
}

void loop()
{
  if ( Serial.available())
  {
    data = Serial.read();

    Serial.print( F( " state " ));
    Serial.print( keyword.whatState( ));
    Serial.print( F( " matched " ));
    printFlash( key[ lowHigh ] );
    Serial.print( F( " so far " ));
    Serial.print( keyword.whatChar( ));
    Serial.print( F( " : serial read is " ));
    if ( data < '0' )
    {
      Serial.print( F( " 0x" ));
      if ( data < 0x10 )
      {
        Serial.print( F( "0" ));
      }
      Serial.print( data, HEX );
    }
    else
    {
      Serial.write((char) data );
    }
    // states: waiting, matching, sub_match, matched, no_match
    if ( keyword.match( data ) == matched ) // matched, found match
    {
      Serial.print( F( " ** Matched! ** " ));

      lowHigh ^= 1; // flips only the low bit
      keyword.setString( key[ lowHigh ] ); // match LOW, HIGH, LOW, HIGH, etc.
    }
    Serial.print( F( " new state " ));
    Serial.print( keyword.whatState( ));
    Serial.println( F( "\n" ));

    if ( keyword.whatState( ) == waiting )
    {
      printUsage();
    }
  }
}